314 lines
8.1 KiB
Go

package day8
import (
"sort"
"strconv"
"strings"
)
type Line struct {
signals []string
answer []string
}
//
// This struct will be used to store the possible corresponding patterns
// A = Top line, B = Left top, ..., I used the same pattern in the AoC problem
//
type Possible struct {
possible []string
}
func Run(dat []byte) (int, int) {
lines := strings.Split(string(dat), "\n")
lineArray := make([]Line, len(lines))
for i, v := range lines {
splitLine := strings.Split(v, " | ")
lineArray[i] = Line{signals: strings.Split(splitLine[0], " "),
answer: strings.Split(splitLine[1], " ")}
}
part1 := 0
for _, value := range lineArray {
for _, signal := range value.answer {
if len(signal) == 2 || len(signal) == 3 || len(signal) == 4 || len(signal) == 7 {
part1++
}
}
}
part2 := 0
for _, line := range lineArray {
currentPossible := make(map[string]Possible)
//#1 - Get the unique ones.
//#1.1 - Get the number one
one := getFromLength(line.signals, 2)[0]
currentPossible[string(one[0])] = Possible{possible: []string{"C", "F"}}
currentPossible[string(one[1])] = Possible{possible: []string{"C", "F"}}
//#1.2 - Get number 7 to work out first link
seven := getFromLength(line.signals, 3)[0]
topLink := getOddLetter(seven, one)[0]
currentPossible[topLink] = Possible{possible: []string{"A"}}
//#1.3 - Get the number 4.
four := getFromLength(line.signals, 4)[0]
oddLetters := getOddLetter(four, one)
currentPossible[oddLetters[0]] = Possible{possible: []string{"B", "D"}}
currentPossible[oddLetters[1]] = Possible{possible: []string{"B", "D"}}
//#1.4 - Get the number 8.
//The 8 will contain the top link which we already know.
eight := getFromLength(line.signals, 7)[0]
oddLetters = getOddLetter(eight, four)
for _, c := range oddLetters {
if c != topLink {
currentPossible[c] = Possible{possible: []string{"E", "G"}}
}
}
//#2 - Get a number with 5 segments on.
// We want to get a 3, which contains all of the 7s
// Choose one that thas the top segment.
fiveLengthNum := getFromLength(line.signals, 5)
three := ""
for _, s := range fiveLengthNum {
contains := true
for _, c := range seven {
if !strings.Contains(s, string(c)) {
contains = false
break
}
}
if contains {
three = s
break
}
}
//#2.1 Extra segments in 3 but not in 7
extraLetters := ""
commonLetters := ""
for _, c := range three {
if !strings.Contains(seven, string(c)) {
extraLetters += string(c)
} else {
if string(c) != topLink {
commonLetters += string(c)
}
}
}
c1 := currentPossible[string(extraLetters[0])].possible[0]
c2 := currentPossible[string(extraLetters[0])].possible[1]
c3 := currentPossible[string(extraLetters[1])].possible[0]
c4 := currentPossible[string(extraLetters[1])].possible[1]
//D first then G
if c1 == "D" && c3 == "G" {
currentPossible[string(extraLetters[0])] = Possible{possible: []string{"D"}}
currentPossible[string(extraLetters[1])] = Possible{possible: []string{"G"}}
} else if c1 == "G" && c3 == "D" {
currentPossible[string(extraLetters[1])] = Possible{possible: []string{"D"}}
currentPossible[string(extraLetters[0])] = Possible{possible: []string{"G"}}
} else if c1 == "D" && c4 == "G" {
currentPossible[string(extraLetters[0])] = Possible{possible: []string{"D"}}
currentPossible[string(extraLetters[1])] = Possible{possible: []string{"G"}}
} else if c1 == "G" && c4 == "D" {
currentPossible[string(extraLetters[1])] = Possible{possible: []string{"D"}}
currentPossible[string(extraLetters[0])] = Possible{possible: []string{"G"}}
} else if c2 == "D" && c3 == "G" {
currentPossible[string(extraLetters[0])] = Possible{possible: []string{"D"}}
currentPossible[string(extraLetters[1])] = Possible{possible: []string{"G"}}
} else if c2 == "G" && c3 == "D" {
currentPossible[string(extraLetters[1])] = Possible{possible: []string{"D"}}
currentPossible[string(extraLetters[0])] = Possible{possible: []string{"G"}}
} else if c2 == "D" && c4 == "G" {
currentPossible[string(extraLetters[0])] = Possible{possible: []string{"D"}}
currentPossible[string(extraLetters[1])] = Possible{possible: []string{"G"}}
} else if c2 == "G" && c4 == "D" {
currentPossible[string(extraLetters[1])] = Possible{possible: []string{"D"}}
currentPossible[string(extraLetters[0])] = Possible{possible: []string{"G"}}
}
purgeMap(currentPossible)
//#3 - Now all that is left is to find C & F
//We can use 6 which contains only contains F
//Get letters of remaining
remainingNotFound := make([]string, 0)
for k, v := range currentPossible {
if len(v.possible) > 1 {
remainingNotFound = append(remainingNotFound, k)
}
}
sixLengthNum := getFromLength(line.signals, 6)
remainingNotFoundLetter := ""
remainingNotFoundLetter2 := ""
for _, possibleNum := range sixLengthNum {
reminingNotFoundNum := 0
for i := 0; i < len(possibleNum); i++ {
if string(possibleNum[i]) == remainingNotFound[0] {
reminingNotFoundNum++
remainingNotFoundLetter = remainingNotFound[0]
remainingNotFoundLetter2 = remainingNotFound[1]
} else if string(possibleNum[i]) == remainingNotFound[1] {
reminingNotFoundNum++
remainingNotFoundLetter = remainingNotFound[1]
remainingNotFoundLetter2 = remainingNotFound[0]
}
}
if reminingNotFoundNum == 1 {
break
}
}
currentPossible[remainingNotFoundLetter] = Possible{possible: []string{"F"}}
currentPossible[remainingNotFoundLetter2] = Possible{possible: []string{"C"}}
answer := ""
for _, testNum := range line.answer {
testNumArray := make([]string, 0)
for i := 0; i < len(testNum); i++ {
testNumArray = append(testNumArray, currentPossible[string(testNum[i])].possible[0])
}
answer += getNum(testNumArray)
}
numAnswer, _ := strconv.Atoi(answer)
part2 += numAnswer
}
return part1, part2
}
func purgeMap(givenMap map[string]Possible) {
singleValues := make([]string, 0)
for _, v := range givenMap {
if len(v.possible) == 1 {
singleValues = append(singleValues, v.possible[0])
}
}
for k, v := range givenMap {
//Check that any of the single values are in v
newValues := make([]string, 0)
for _, singleValue := range v.possible {
if !containsInArray(singleValues, singleValue) {
newValues = append(newValues, singleValue)
}
}
if len(newValues) > 0 {
givenMap[k] = Possible{possible: newValues}
}
}
}
func getNum(arr []string) string {
sort.Strings(arr)
if equals(arr, []string{"A", "B", "C", "E", "F", "G"}) {
return "0"
} else if equals(arr, []string{"C", "F"}) {
return "1"
} else if equals(arr, []string{"A", "C", "D", "E", "G"}) {
return "2"
} else if equals(arr, []string{"A", "C", "D", "F", "G"}) {
return "3"
} else if equals(arr, []string{"B", "D", "C", "F"}) {
return "4"
} else if equals(arr, []string{"A", "B", "D", "F", "G"}) {
return "5"
} else if equals(arr, []string{"A", "B", "D", "E", "F", "G"}) {
return "6"
} else if equals(arr, []string{"A", "C", "F"}) {
return "7"
} else if equals(arr, []string{"A", "B", "C", "D", "E", "F", "G"}) {
return "8"
} else if equals(arr, []string{"A", "B", "C", "D", "F", "G"}) {
return "9"
}
return ""
}
func getFromLength(arr []string, length int) []string {
returnArray := make([]string, 0)
for _, v := range arr {
if len(v) == length {
returnArray = append(returnArray, v)
}
}
return returnArray
}
//First parameter is the bigger array
func getOddLetter(string1 string, string2 string) []string {
returnArray := make([]string, 0)
for _, v := range string1 {
if !contains(string2, v) {
returnArray = append(returnArray, string(v))
}
}
return returnArray
}
func contains(givenString string, value rune) bool {
for _, v := range givenString {
if v == value {
return true
}
}
return false
}
func containsInArray(givenArr []string, char string) bool {
for _, v := range givenArr {
if v == char {
return true
}
}
return false
}
func equals(arr1 []string, arr2 []string) bool {
sort.Strings(arr1)
sort.Strings(arr2)
for i, v := range arr1 {
if v != arr2[i] {
return false
}
}
return true
}