day 8 work and jumbled together

This commit is contained in:
RussellChamp committed 2021-12-08 16:31:05 -05:00
1 parent e76cb1a355
commit c7ea0afedc
18 files changed
+457 -21

No files matched your search

+2 -2
View File
@@ -37,8 +37,8 @@ func part1() {
fmt.Println(" Answer: How many measurements are larger than the previous measurement?")
input, err := os.Open("./input.txt")
defer input.Close()
check(err)
defer input.Close()
scanner := bufio.NewScanner(input)
@@ -67,8 +67,8 @@ func part2() {
fmt.Println(" Answer: How many sums are larger than the previous sum?")
input, err := os.Open("./input.txt")
defer input.Close()
check(err)
defer input.Close()
scanner := bufio.NewScanner(input)
+3 -3
View File
@@ -1,14 +1,14 @@
package main
import (
assert "AoC2021/utils/testing"
"AoC2021/utils/assert"
"testing"
)
func TestPrintPip(t *testing.T) {
DEBUG = false
str := GetPip(1, 1, 1, 100)
assert.StrEquals(t, str, "", "Should return an empty string")
assert.EqualsStr(t, str, "", "Should return an empty string")
DEBUG = true
cases := []struct {
@@ -24,7 +24,7 @@ func TestPrintPip(t *testing.T) {
for _, c := range cases {
str := GetPip(c.first, c.previous, c.current, c.total)
assert.StrEquals(t, str, c.expected, c.msg)
assert.EqualsStr(t, str, c.expected, c.msg)
}
}
+2 -2
View File
@@ -1,11 +1,11 @@
package main
import (
assert "AoC2021/utils/testing"
"AoC2021/utils/assert"
"testing"
)
func TestDay2(t *testing.T) {
assert.IsTrue(t, true, "cool")
assert.StrEquals(t, "foo", "bar", "baz")
assert.EqualsStr(t, "foo", "bar", "baz")
}
+2 -2
View File
@@ -1,11 +1,11 @@
package main
import (
assert "AoC2021/utils/testing"
"AoC2021/utils/assert"
"testing"
)
func TestDay3(t *testing.T) {
assert.IsTrue(t, true, "cool")
assert.StrEquals(t, "foo", "bar", "baz")
assert.EqualsStr(t, "foo", "bar", "baz")
}
+2 -2
View File
@@ -1,11 +1,11 @@
package main
import (
assert "AoC2021/utils/testing"
"AoC2021/utils/assert"
"testing"
)
func TestDay4(t *testing.T) {
assert.IsTrue(t, true, "cool")
assert.StrEquals(t, "foo", "bar", "baz")
assert.EqualsStr(t, "foo", "bar", "baz")
}
+2 -2
View File
@@ -1,11 +1,11 @@
package main
import (
assert "AoC2021/utils/testing"
"AoC2021/utils/assert"
"testing"
)
func TestDay6(t *testing.T) {
assert.IsTrue(t, true, "cool")
assert.StrEquals(t, "foo", "bar", "baz")
assert.EqualsStr(t, "foo", "bar", "baz")
}
+2 -2
View File
@@ -1,11 +1,11 @@
package main
import (
assert "AoC2021/utils/testing"
"AoC2021/utils/assert"
"testing"
)
func TestDay7(t *testing.T) {
assert.IsTrue(t, true, "cool")
assert.StrEquals(t, "foo", "bar", "baz")
assert.EqualsStr(t, "foo", "bar", "baz")
}
+232
View File
@@ -0,0 +1,232 @@
/* Day 8: Seven Segment Search */
package main
import (
"AoC2021/utils/arrays"
"AoC2021/utils/bits"
"AoC2021/utils/log"
"AoC2021/utils/timer"
"bufio"
"math"
"os"
"strings"
)
func check(e error) {
if e != nil {
log.Println(log.DEBUG, "Oh snap!")
panic(e)
}
}
func main() {
log.Println(log.NORMAL, "--- Day 8: Seven Segment Search ---")
timer.Start()
part1()
timer.Tick()
log.Println(log.NORMAL)
part2()
timer.Tick()
log.Println(log.NORMAL)
}
func part1() {
log.Println(log.NORMAL, "* Part 1 *")
log.Println(log.NORMAL, " Goal: For now, focus on the easy digits.")
log.Println(log.NORMAL, " Answer: In the output values, how many times do digits 1, 4, 7, or 8 appear?")
input, err := os.Open("./input.txt")
check(err)
defer input.Close()
scanner := bufio.NewScanner(input)
lines := 0
ones := 0
fours := 0
sevens := 0
eights := 0
for scanner.Scan() {
fields := strings.Split(scanner.Text(), " | ")
if len(fields) != 2 {
panic("Read invalid input line")
}
_ = strings.Fields(fields[0])
// count all the ones, fours, sevens, and eights in the output strings based on how many segments are lit up
outputDigits := strings.Fields(fields[1])
for _, d := range outputDigits {
switch len(d) {
case 2:
ones += 1
case 3:
sevens += 1
case 4:
fours += 1
case 7:
eights += 1
}
}
lines++
}
total := ones + fours + sevens + eights
log.Printf(log.NORMAL, "Read %d lines from input\n", lines)
log.Printf(log.NORMAL, "We counted %d ones, %d fours, %d sevens, and %d eights for a total of %d\n", ones, fours, sevens, eights, total)
}
func part2() {
log.Println(log.NORMAL, "* Part 2 *")
log.Println(log.NORMAL, " Goal: For each entry, determine all of the wire/segment connections and decode the four-digit output values")
log.Println(log.NORMAL, " Answer: What do you get if you add up all of the output values?")
input, err := os.Open("./input.txt")
check(err)
defer input.Close()
scanner := bufio.NewScanner(input)
lines := 0
total := 0
for scanner.Scan() {
fields := strings.Split(scanner.Text(), " | ")
if len(fields) != 2 {
panic("Read invalid input line")
}
inputFields := strings.Fields(fields[0])
segMap := createPatternMap(arrays.MapStrToInt(inputFields, ParseField))
log.Println(log.DEBUG, "Created pattern", segMap)
outputFields := strings.Fields(fields[1])
fieldValue := 0
fieldsCount := len(outputFields)
for idx, field := range outputFields {
fValue := segMap[ParseField(field)]
log.Printf(log.DIAGNOSTIC, "field %s => %d; ", field, fValue)
fieldValue += fValue * int(math.Pow10(fieldsCount-idx-1))
}
total += fieldValue
log.Println(log.DIAGNOSTIC)
log.Printf(log.DIAGNOSTIC, "Unscrambled %s => %d\n", outputFields, fieldValue)
lines++
}
log.Printf(log.NORMAL, "Read %d lines from input\n", lines)
log.Printf(log.NORMAL, "Calculated total is %d\n", total)
}
// Represent a segment as a 7-bit value
/* Segment values are aligned as below
* aaaa
* b c
* b c
* dddd
* e f
* e f
* gggg
* segment 'a' is found in [0, 2, 3, 5, 6, 7, 8, 9] but not [1, 4]
* segment 'b' is found in [0, 4, 5, 6, 8, 9] but not [1, 2, 3, 7]
* segment 'c' is found in [0, 1, 2, 3, 4, 7, 8, 9] but not [5, 6]
* segment 'd' is found in [2, 3, 4, 5, 6, 8, 9] but not [0, 1, 7]
* segment 'e' is found in [0, 2, 6, 8] but not [1, 3, 4, 5, 7, 9]
* segment 'f' is found in [0, 1, 3, 4, 5, 6, 7, 8, 9] but not [2]
* segment 'g' is found in [0, 2, 3, 5, 6, 8, 9] but not [1, 4, 7]
*/
// parse a field from a string value to a bit-map int value (eg 'cda' to b1101 => 13)
func ParseField(field string) int {
value := 0
for _, c := range field {
value += 1 << int(rune(c)-rune('a'))
}
return value
}
// func getDefaultMap() map[int]int {
// return map[int]int{
// ParseField("abcefg"): 0,
// ParseField("cf"): 1,
// ParseField("acdeg"): 2,
// ParseField("acdfg"): 3,
// ParseField("bcdf"): 4,
// ParseField("abdfg"): 5,
// ParseField("abdefg"): 6,
// ParseField("acf"): 7,
// ParseField("abcdefg"): 8,
// ParseField("abcdfg"): 9}
// }
// Given a list of a all 10 possible 7-segment displays create a map that can be used to unscramble values
func createPatternMap(input []int) map[int]int {
// initialize our translation map that will convert from character to character (eg wire 'a' actually goes to segment 'c')
segMap := make(map[int]int)
// first find and map all the values that can be known by counting segments
// find the only pattern that matches '1'
one := arrays.FindFirstInt(input, bits.ByBitCount(2))
// next find the one that matches '7'
seven := arrays.FindFirstInt(input, bits.ByBitCount(3))
// next find the one that matches '4'
four := arrays.FindFirstInt(input, bits.ByBitCount(4))
// the one that matches '8' will be the only one with 7 segments but tells us nothing about what those segments map to
eight := arrays.FindFirstInt(input, bits.ByBitCount(7))
// the two segments in '1' will match 'c' or 'f' but we don't know yet
// the additional segment in '7' will map to segment 'a'
segMap['a'] = seven - one
// the two additional segments in '4' will match 'b' or 'd' but we're not sure which ones yet
// all of '0', '6' and '9' have 6 segments
// the six-segment field that shares only 1 side with '1' is '6'
six := arrays.FindFirstInt(input, func(v int) bool { return bits.ByBitCount(6)(v) && bits.MatchingOnBits(one, v) == 1 })
// and we know that the segment not in six is 'c'
segMap['c'] = eight - six
// and the other value 'f' will be the other segment in '1'
segMap['f'] = one - segMap['c']
// but we can't yet place '0' or '9' with what we know
// all of '2', '3', and '5' have 5 segments
// three is the only 5 segment field that matches 3 bits in '7'
three := arrays.FindFirstInt(input, func(v int) bool { return bits.ByBitCount(5)(v) && bits.MatchingOnBits(seven, v) == 3 })
// the additional two segments are 'd' ang 'g' but we can't place them yet
// five is the other 5 segment field that matches 3 bits in '4'
five := arrays.FindFirstInt(input, func(v int) bool { return bits.ByBitCount(5)(v) && v != three && bits.MatchingOnBits(four, v) == 3 })
// segments 'b', 'd', and 'g' can't yet be placed
// but segment 'e' is what we get by subtracting five from six
segMap['e'] = six - five
// the last five segment number must be 2
two := arrays.FindFirstInt(input, func(v int) bool { return bits.ByBitCount(5)(v) && v != three && v != five })
// segment 'g' is the only one that doesn't appear in '1', '4', or '7' and is not 'e'
segMap['g'] = eight - bits.BitwiseAndA(one, four, seven, segMap['e'])
// segment 'd' is '3' minus 'a', 'c', 'f', and 'g'
segMap['d'] = three - segMap['a'] - segMap['c'] - segMap['f'] - segMap['g']
// then we know that zero is eight minus 'd'
zero := eight - segMap['d']
// and nine will be eight minus 'e'
nine := eight - segMap['e']
log.Println(log.DIAGNOSTIC, "segMap", segMap)
return map[int]int{
zero: 0,
one: 1,
two: 2,
three: 3,
four: 4,
five: 5,
six: 6,
seven: 7,
eight: 8,
nine: 9,
}
}
+10
View File
@@ -0,0 +1,10 @@
package main
import (
"AoC2021/utils/assert"
"testing"
)
func TestDay8ParseField(t *testing.T) {
assert.EqualsInt(t, ParseField("abc"), 7, "should parse fields correctly")
}
+1 -1
View File
@@ -1,7 +1,7 @@
package main
import (
assert "AoC2021/utils/testing"
"AoC2021/utils/assert"
"testing"
)
+4 -1
View File
@@ -11,4 +11,7 @@ build:
cd day-$(day); go run .
test:
cd day-$(day); go test
cd day-$(day); go test
test-utils:
go test ./utils/*
+49
View File
@@ -0,0 +1,49 @@
package arrays
func FindFirst(list []string, filter func(string) bool) string {
for _, item := range list {
if filter(item) {
return item
}
}
panic("No item found that matches criteria")
}
func FindFirstInt(list []int, filter func(int) bool) int {
for _, item := range list {
if filter(item) {
return item
}
}
panic("No item found that matches criteria")
}
func FindAll(list []string, filter func(string) bool) []string {
var filteredList []string
for _, item := range list {
if filter(item) {
filteredList = append(filteredList, item)
}
}
return filteredList
}
func FindAllInt(list []int, filter func(int) bool) []int {
var filteredList []int
for _, item := range list {
if filter(item) {
filteredList = append(filteredList, item)
}
}
return filteredList
}
func MapStrToInt(input []string, fn func(string) int) []int {
var retVals []int
for _, s := range input {
retVals = append(retVals, fn(s))
}
return retVals
}
+47
View File
@@ -0,0 +1,47 @@
package arrays
import (
"AoC2021/utils/assert"
"testing"
)
func TestFindFirst(t *testing.T) {
list := []string{"foo", "bar", "baz"}
filter := func(s string) bool { return s == "baz" }
item := FindFirst(list, filter)
assert.EqualsStr(t, item, "baz", "should find item baz")
}
func TestFindFirstInt(t *testing.T) {
list := []int{1, 3, 4, 5}
filter := func(i int) bool { return i%2 == 0 }
item := FindFirstInt(list, filter)
assert.EqualsInt(t, item, 4, "should find even number")
}
func TestFindAll(t *testing.T) {
list := []string{"hello", "world", "foo"}
filter := func(s string) bool { return len(s) > 3 }
items := FindAll(list, filter)
assert.EqualsInt(t, len(items), 2, "should filter lists of strings")
}
func TestFindAllInt(t *testing.T) {
list := []int{1, 3, 99, 200, 9000}
filter := func(i int) bool { return i > 50 }
items := FindAllInt(list, filter)
assert.EqualsInt(t, len(items), 3, "should filter lists of ints")
}
func TestMapStrToInt(t *testing.T) {
list := []string{"alice", "bob", "carol"}
mapFn := func(s string) int { return int(s[0]) }
items := MapStrToInt(list, mapFn)
assert.EqualsInt(t, items[0], int('a'), "should run map function across all strings")
}
@@ -9,9 +9,16 @@ func IsTrue(t *testing.T, expr bool, message string) {
}
}
func StrEquals(t *testing.T, actual, expected, message string) {
func EqualsStr(t *testing.T, actual, expected, message string) {
if actual != expected {
t.Logf("Expected value '%s' but got '%s': %s", expected, actual, message)
t.Fail()
}
}
func EqualsInt(t *testing.T, actual, expected int, message string) {
if actual != expected {
t.Logf("Expected value '%d' but got '%d': %s", expected, actual, message)
t.Fail()
}
}
+54
View File
@@ -0,0 +1,54 @@
package bits
func GetBitCount(val int) int {
count := 0
for val != 0 {
count += val & 1
val >>= 1
}
return count
}
// returns the number of matching bits in the two value's binary expression
func MatchingOnBits(v1 int, v2 int) int {
count := 0
for v1 != 0 && v2 != 0 {
if v1&1 == 1 && v2&1 == 1 {
count += 1
}
v1 >>= 1
v2 >>= 1
}
return count
}
func BitwiseAnd(v1 int, v2 int) int {
val := 0
for block := 1; v1 != 0 || v2 != 0; block <<= 1 {
if v1&1 == 1 || v2&1 == 1 {
val += block
}
if v1 > 0 {
v1 >>= 1
}
if v2 > 0 {
v2 >>= 1
}
}
return val
}
func BitwiseAndA(a ...int) int {
total := 0
for _, i := range a {
total = BitwiseAnd(total, i)
}
return total
}
func ByBitCount(size int) func(int) bool {
return func(val int) bool { return GetBitCount(val) == size }
}
+28
View File
@@ -0,0 +1,28 @@
package bits
import (
"AoC2021/utils/assert"
"testing"
)
func TestGetBitCount(t *testing.T) {
assert.EqualsInt(t, GetBitCount(0), 0, "should count zero bits")
assert.EqualsInt(t, GetBitCount(15), 4, "should count bits")
assert.EqualsInt(t, GetBitCount(16), 1, "should not count empty bits")
}
func TestMatchingOnBits(t *testing.T) {
assert.EqualsInt(t, MatchingOnBits(5 /*101*/, 13 /*1101*/), 2, "should match 2 bits")
}
func TestBitwiseAnd(t *testing.T) {
assert.EqualsInt(t, BitwiseAnd(7 /*111*/, 18 /*10010*/), 23 /*10111*/, "should bitwise add")
}
func TestBitwiseAndA(t *testing.T) {
assert.EqualsInt(t, BitwiseAndA(1, 4, 8, 12), 13 /*1101*/, "should bitwise add lists")
}
func TestByBitCount(t *testing.T) {
twoBitFilter := ByBitCount(2)
assert.IsTrue(t, twoBitFilter(17 /*10001*/), "should count bits")
}
+5
View File
@@ -0,0 +1,5 @@
package filters
func ByLength(size int) func(string) bool {
return func(val string) bool { return len(val) == size }
}
+4 -3
View File
@@ -3,9 +3,10 @@ package log
import "fmt"
const (
NONE = 0
NORMAL = 1
DEBUG = 2
NONE = 0
NORMAL = 1
DEBUG = 2
DIAGNOSTIC = 3
)
var LOG_LEVEL = NORMAL