adventofcode/2021/day08/main.go

275 lines
5.9 KiB
Go

package main
import (
"bytes"
"fmt"
"sort"
"strings"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
func main() {
inp := h.StdinToStringSlice()
solve(inp)
}
func solve(inp []string) {
var segments []seg
for i := range inp {
pts := strings.Split(inp[i], "|")
var patterns [][]byte
for _, v := range strings.Fields(pts[0]) {
patterns = append(patterns, []byte(v))
}
var output [][]byte
for _, v := range strings.Fields(pts[1]) {
output = append(output, []byte(v))
}
segments = append(segments, DeduceSeg(patterns, output))
}
var c1, c4, c7, c8 int
var total int
for i := range segments {
c1 += segments[i].instances(1)
c4 += segments[i].instances(4)
c7 += segments[i].instances(7)
c8 += segments[i].instances(8)
total += segments[i].value()
}
fmt.Println("# Part 1")
fmt.Println("1,4,7,8 appear", c1+c4+c7+c8, "times")
fmt.Println("")
fmt.Println("# Part 2")
fmt.Println("Total", total)
}
type seg struct {
N, NE, SE, S, SW, NW, C byte
Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine []byte
Output []int
}
func DeduceSeg(pInput, oInput [][]byte) seg {
segments := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'}
// 0, 3, 5, 9
var patterns [][]byte
for _, p := range pInput {
sort.Slice(p, func(i int, j int) bool { return p[i] < p[j] })
patterns = append(patterns, p)
}
var output [][]byte
for _, o := range oInput {
sort.Slice(o, func(i int, j int) bool { return o[i] < o[j] })
output = append(output, o)
}
var removeFromSegments = func(v ...byte) {
for k := range v {
rem := -1
for b := range segments {
if segments[b] == v[k] {
rem = b
}
}
if rem > -1 {
segments = append(segments[:rem], segments[rem+1:]...)
}
}
}
var removeFromPatterns = func(v ...[]byte) {
for k := range v {
rem := -1
for i := range patterns {
if string(patterns[i]) == string(v[k]) {
rem = i
}
}
if rem > -1 {
patterns = append(patterns[:rem], patterns[rem+1:]...)
}
}
}
s := seg{
N: '-',
NE: '-',
SE: '-',
S: '-',
SW: '-',
NW: '-',
C: '-',
}
for i := range patterns {
switch {
case len(patterns[i]) == 2:
s.One = patterns[i]
case len(patterns[i]) == 4:
s.Four = patterns[i]
case len(patterns[i]) == 3:
s.Seven = patterns[i]
case len(patterns[i]) == 7:
s.Eight = patterns[i]
}
}
removeFromPatterns(s.One, s.Four, s.Seven, s.Eight)
//First, we know the N segment, it's what shows up in 'seven' , but not in 'one'
for i := range s.Seven {
if bytes.IndexByte(s.One, s.Seven[i]) == -1 {
s.N = s.Seven[i]
removeFromSegments(s.N)
break
}
}
// Six length numbers:
// 0, 9, 6
// We know that 'seven' is inside of 'nine' and 'zero' but not 'six'
for _, p := range patterns {
if len(p) == 6 {
for _, v := range s.Seven {
if bytes.IndexByte(p, v) == -1 {
// 'v' also has to be the NE segment
s.NE = v
removeFromSegments(s.NE)
s.Six = p
break
}
}
}
}
removeFromPatterns(s.Six)
for _, v := range s.Seven {
if v != s.N && v != s.NE {
s.SE = v
removeFromSegments(s.SE)
}
}
// Five length numbers: 2, 3, 5
// Two is missing the SE segment
// Five is missing the NE segment
for _, p := range patterns {
if len(p) == 5 {
if bytes.IndexByte(p, s.SE) == -1 {
s.Two = p
}
if bytes.IndexByte(p, s.NE) == -1 {
s.Five = p
}
}
}
removeFromPatterns(s.Two, s.Five)
// The remaining 5 length is Three
for _, p := range patterns {
if len(p) == 5 {
s.Three = p
break
}
}
removeFromPatterns(s.Three)
// Nine is the same as Five except for NE
for _, p := range patterns {
for _, b := range p {
if b != s.NE && bytes.IndexByte(s.Five, b) == -1 {
s.Zero = p
break
}
}
}
removeFromPatterns(s.Zero)
s.Nine = patterns[0]
// NW is the non-SE that is missing from Two
for _, b := range segments {
if b != s.SE && bytes.IndexByte(s.Two, b) == -1 {
s.NW = b
removeFromSegments(s.NW)
break
}
}
// SW is the segment in Six that's not in Five
for _, b := range segments {
if bytes.IndexByte(s.Five, b) == -1 && bytes.IndexByte(s.Six, b) != -1 {
s.SW = b
removeFromSegments(s.SW)
}
}
// C is the remaining segment in Four that's not in 0
for _, b := range segments {
if bytes.IndexByte(s.Zero, b) == -1 && bytes.IndexByte(s.Four, b) != -1 {
s.C = b
removeFromSegments(s.C)
}
}
// The last segment is S
s.S = segments[0]
// Now fill in the outputs
for i := range output {
s.Output = append(s.Output, s.valueOf(output[i]))
}
return s
}
func (s seg) instances(v int) int {
var t int
for _, o := range s.Output {
if o == v {
t++
}
}
return t
}
func (s seg) value() int {
var retStr string
for _, o := range s.Output {
retStr = retStr + h.Itoa(o)
}
return h.Atoi(retStr)
}
func (s seg) valueOf(b []byte) int {
sort.Slice(b, func(i int, j int) bool { return b[i] < b[j] })
switch string(b) {
case string(s.Zero):
return 0
case string(s.One):
return 1
case string(s.Two):
return 2
case string(s.Three):
return 3
case string(s.Four):
return 4
case string(s.Five):
return 5
case string(s.Six):
return 6
case string(s.Seven):
return 7
case string(s.Eight):
return 8
case string(s.Nine):
return 9
}
return -1
}
func (s seg) String() string {
ret := fmt.Sprintf("%s\n", string([]byte{' ', s.N, s.N, s.N, s.N, ' '}))
ret = fmt.Sprintf("%s%s\n", ret, string([]byte{s.NW, ' ', ' ', ' ', ' ', s.NE}))
ret = fmt.Sprintf("%s%s\n", ret, string([]byte{s.NW, ' ', ' ', ' ', ' ', s.NE}))
ret = fmt.Sprintf("%s%s\n", ret, string([]byte{' ', s.C, s.C, s.C, s.C, ' '}))
ret = fmt.Sprintf("%s%s\n", ret, string([]byte{s.SW, ' ', ' ', ' ', ' ', s.SE}))
ret = fmt.Sprintf("%s%s\n", ret, string([]byte{s.SW, ' ', ' ', ' ', ' ', s.SE}))
ret = fmt.Sprintf("%s%s\n", ret, string([]byte{' ', s.S, s.S, s.S, s.S, ' '}))
return ret
}