mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-06-07 20:53:30 +02:00
2021 day8 2852/1511, i am too lazy to clean this up... oof that was painful
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/alexchao26/advent-of-code-go/util"
|
||||
)
|
||||
|
||||
//go:embed input.txt
|
||||
var input string
|
||||
|
||||
func init() {
|
||||
// do this in init (not main) so test file has same input
|
||||
input = strings.TrimRight(input, "\n")
|
||||
if len(input) == 0 {
|
||||
panic("empty input.txt file")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var part int
|
||||
flag.IntVar(&part, "part", 1, "part 1 or 2")
|
||||
flag.Parse()
|
||||
fmt.Println("Running part", part)
|
||||
|
||||
ans := jumbledSevenSegment(input, part)
|
||||
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
||||
fmt.Println("Output:", ans)
|
||||
}
|
||||
|
||||
func jumbledSevenSegment(input string, part int) int {
|
||||
// four digit seven segment displays
|
||||
// seven segments are letters
|
||||
// need to unjumble the ten mappings (pre-bar) to outputs (last 4 post-bar)
|
||||
var parsedInput [][]string
|
||||
for i, line := range strings.Split(input, "\n") {
|
||||
// regexp capture group to just get characters
|
||||
parts := regexp.MustCompile(`([a-g]+)`).FindAllString(line, -1)
|
||||
|
||||
if len(parts) != 14 {
|
||||
log.Fatalf("should be 14 parts in each input line, got %d for line %d", len(parts), i)
|
||||
}
|
||||
|
||||
// return them as just all 14 mappings together, alphabetize them for easier comparison later
|
||||
var fourteen []string
|
||||
for _, v := range parts {
|
||||
fourteen = append(fourteen, alphabetizeString(v))
|
||||
}
|
||||
parsedInput = append(parsedInput, fourteen)
|
||||
}
|
||||
|
||||
// PART 1 just count the outputs that are "easy" to determine
|
||||
// aka only one displayed number uses that number of segments
|
||||
if part == 1 {
|
||||
var ans int
|
||||
for _, set := range parsedInput {
|
||||
// check the back 4: output
|
||||
for _, o := range set[10:] {
|
||||
switch len(o) {
|
||||
// 1, 4, 7, 8 have a unique number of segments lit
|
||||
// 2, 4, 4, 7 segments respectively
|
||||
case 2, 4, 3, 7:
|
||||
ans++
|
||||
}
|
||||
}
|
||||
}
|
||||
return ans
|
||||
}
|
||||
|
||||
// PART 2
|
||||
var ans int
|
||||
indexToCharacters := make([]string, 10)
|
||||
for _, set := range parsedInput {
|
||||
|
||||
workingSet := set[:10]
|
||||
var killIndices []int // store the indices that will need to be removed in batches
|
||||
|
||||
// 1, 4, 7 and 8 are all easy to find because they have a unique number of segments
|
||||
for i, mapping := range workingSet {
|
||||
switch len(mapping) {
|
||||
case 2:
|
||||
// these two make up the 1
|
||||
indexToCharacters[1] = mapping
|
||||
killIndices = append(killIndices, i)
|
||||
case 4: // the 4
|
||||
indexToCharacters[4] = mapping
|
||||
killIndices = append(killIndices, i)
|
||||
case 3: // the 7
|
||||
indexToCharacters[7] = mapping
|
||||
killIndices = append(killIndices, i)
|
||||
case 7: // the 8
|
||||
indexToCharacters[8] = mapping
|
||||
killIndices = append(killIndices, i)
|
||||
}
|
||||
}
|
||||
|
||||
// remove them from the workingSet
|
||||
workingSet = removeSliceIndices(workingSet, killIndices...)
|
||||
|
||||
// only 0 3 and 9 will fully overlap the 1 characters
|
||||
var zeroThreeOrNine []string
|
||||
killIndices = []int{} // reset these...
|
||||
for i, mapping := range workingSet {
|
||||
if checkStringOverlap(mapping, indexToCharacters[1]) {
|
||||
zeroThreeOrNine = append(zeroThreeOrNine, mapping)
|
||||
killIndices = append(killIndices, i)
|
||||
}
|
||||
}
|
||||
if len(zeroThreeOrNine) != 3 {
|
||||
log.Fatalf("one three or nine does not have three matches: got %d", len(zeroThreeOrNine))
|
||||
}
|
||||
|
||||
// find the 3 based on segment length
|
||||
for i, maybe039 := range zeroThreeOrNine {
|
||||
if len(maybe039) == 5 {
|
||||
// must be the 3
|
||||
indexToCharacters[3] = maybe039
|
||||
zeroThreeOrNine = removeSliceIndices(zeroThreeOrNine, i)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// the 9 will have a full overlap with 4
|
||||
for i, maybe09 := range zeroThreeOrNine {
|
||||
if checkStringOverlap(maybe09, indexToCharacters[4]) {
|
||||
indexToCharacters[9] = maybe09
|
||||
zeroThreeOrNine = removeSliceIndices(zeroThreeOrNine, i)
|
||||
}
|
||||
}
|
||||
|
||||
// 0 is only one left of the triplet
|
||||
indexToCharacters[0] = zeroThreeOrNine[0]
|
||||
|
||||
// trim down working set again
|
||||
workingSet = removeSliceIndices(workingSet, killIndices...)
|
||||
if len(workingSet) != 3 {
|
||||
log.Fatalf("expected length of 3 at this stage, got %d", len(workingSet))
|
||||
}
|
||||
|
||||
// 6 is the last one with a length of 6
|
||||
for i, mapping := range workingSet {
|
||||
if len(mapping) == 6 {
|
||||
indexToCharacters[6] = mapping
|
||||
workingSet = removeSliceIndices(workingSet, i)
|
||||
}
|
||||
}
|
||||
|
||||
// 5 will be fully contained within the 9
|
||||
for i, mapping := range workingSet {
|
||||
if checkStringOverlap(indexToCharacters[9], mapping) {
|
||||
indexToCharacters[5] = mapping
|
||||
workingSet = removeSliceIndices(workingSet, i)
|
||||
}
|
||||
}
|
||||
|
||||
if len(workingSet) != 1 {
|
||||
log.Fatalf("expected length of 1 at this stage, got %d", len(workingSet))
|
||||
}
|
||||
|
||||
// 2 is the last remaining mapping
|
||||
indexToCharacters[2] = workingSet[0]
|
||||
|
||||
// finally, collect the four digits in the output & add it to the answer
|
||||
var num int
|
||||
for _, out := range set[10:] {
|
||||
for i, mapping := range indexToCharacters {
|
||||
// because they were all alphabetized, we can just do a direct comparison
|
||||
if out == mapping {
|
||||
// shift all digits to the left and add the new digit to the end
|
||||
num *= 10
|
||||
num += i
|
||||
}
|
||||
}
|
||||
}
|
||||
ans += num
|
||||
}
|
||||
|
||||
return ans
|
||||
}
|
||||
|
||||
func removeSliceIndices(sli []string, indices ...int) []string {
|
||||
m := map[int]bool{}
|
||||
for _, v := range indices {
|
||||
m[v] = true
|
||||
}
|
||||
|
||||
var ans []string
|
||||
for i, v := range sli {
|
||||
if !m[i] {
|
||||
ans = append(ans, v)
|
||||
}
|
||||
}
|
||||
return ans
|
||||
}
|
||||
|
||||
func checkStringOverlap(larger, smaller string) bool {
|
||||
// safeguard, a smaller string can never contain a larger string
|
||||
// i think every use of this function already passes args in the correct order, but this makes
|
||||
// the checkStringOverlap algo more robust in general
|
||||
if len(larger) < len(smaller) {
|
||||
larger, smaller = smaller, larger
|
||||
}
|
||||
|
||||
largeMap := map[rune]bool{}
|
||||
for _, r := range larger {
|
||||
largeMap[r] = true
|
||||
}
|
||||
|
||||
// check all runes in the smaller string, return false if not in largeMap
|
||||
for _, r := range smaller {
|
||||
if !largeMap[r] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// alphabetize the strings to make them easier to compare later
|
||||
func alphabetizeString(input string) string {
|
||||
chars := strings.Split(input, "")
|
||||
sort.Strings(chars)
|
||||
return strings.Join(chars, "")
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var shortExample = `acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf`
|
||||
var example = `be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe
|
||||
edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc
|
||||
fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg
|
||||
fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb
|
||||
aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea
|
||||
fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb
|
||||
dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe
|
||||
bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef
|
||||
egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb
|
||||
gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce`
|
||||
|
||||
func Test_jumbledSevenSegment(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
part int
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "example",
|
||||
input: example,
|
||||
part: 1,
|
||||
want: 26,
|
||||
},
|
||||
{
|
||||
name: "actual",
|
||||
input: input,
|
||||
part: 1,
|
||||
want: 367,
|
||||
},
|
||||
{
|
||||
name: "short_example",
|
||||
input: shortExample,
|
||||
part: 2,
|
||||
want: 5353,
|
||||
},
|
||||
{
|
||||
name: "example",
|
||||
input: example,
|
||||
part: 2,
|
||||
want: 61229,
|
||||
},
|
||||
{
|
||||
name: "actual",
|
||||
input: input,
|
||||
part: 2,
|
||||
want: 974512,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := jumbledSevenSegment(tt.input, tt.part); got != tt.want {
|
||||
t.Errorf("jumbledSevenSegment() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user