Watch
1
0
Fork
You've already forked advent-of-code
0

2021 Day 8

This commit is contained in:
Jeremy Kaplan 2021-12-07 23:42:54 -08:00
commit 2d203e541e
3 changed files with 336 additions and 0 deletions

View file

@ -0,0 +1,108 @@
package stringset
import (
"fmt"
"sort"
"strings"
)
type StringSet map[string]struct{}
func Slice(vals []string) StringSet {
set := make(StringSet)
for _, v := range vals {
set[v] = struct{}{}
}
return set
}
func Runes(v string) StringSet {
return Slice(strings.Split(v, ""))
}
func (s StringSet) String() string {
var elts []string
for e := range s {
elts = append(elts, e)
}
sort.Strings(elts)
return fmt.Sprintf("{%s}", strings.Join(elts, ", "))
}
func (s StringSet) Only() string {
if len(s) > 1 {
panic(fmt.Sprintf("too many elements: %s", s))
}
for v := range s {
return v
}
panic("empty set")
}
func (s StringSet) Has(elt string) bool {
_, ok := s[elt]
return ok
}
func (s StringSet) add(elt string) {
s[elt] = struct{}{}
}
func (s StringSet) Union(t StringSet) StringSet {
set := make(StringSet)
for a := range s {
set.add(a)
}
for b := range t {
set.add(b)
}
return set
}
func (s StringSet) Intersect(t StringSet) StringSet {
set := make(StringSet)
for a := range s {
if t.Has(a) {
set.add(a)
}
}
return set
}
func (s StringSet) Minus(t StringSet) StringSet {
set := make(StringSet)
for a := range s {
if !t.Has(a) {
set.add(a)
}
}
return set
}
func (s StringSet) Equal(t StringSet) bool {
if len(s) != len(t) {
return false
}
for a := range s {
if !t.Has(a) {
return false
}
}
return true
}
func Union(sets ...StringSet) StringSet {
t := make(StringSet)
for _, s := range sets {
t = t.Union(s)
}
return t
}
func Intersect(sets ...StringSet) StringSet {
t := Union(sets...)
for _, s := range sets {
t = t.Intersect(s)
}
return t
}

View file

@ -0,0 +1,40 @@
package stringset_test
import (
"testing"
"github.com/jdkaplan/advent-of-code/aoc/stringset"
)
func TestMinus(t *testing.T) {
a := stringset.Runes("dab")
b := stringset.Runes("ab")
u := a.Minus(b)
if n := len(u); n != 1 {
t.Fatalf("Expected one value, got %d", n)
}
if v := u.Only(); v != "d" {
t.Errorf(`Expected value to be "d", got %s`, v)
}
}
func TestIntersect(t *testing.T) {
t.Run("pairwise", func(t *testing.T) {
u := stringset.Runes("dab").Intersect(stringset.Runes("ab"))
expected := stringset.Runes("ab")
if !u.Equal(expected) {
t.Errorf(`Expected value to be %s, got %s`, expected, u)
}
})
t.Run("multiple", func(t *testing.T) {
u := stringset.Intersect(
stringset.Runes("dab"),
stringset.Runes("ab"),
)
expected := stringset.Runes("ab")
if !u.Equal(expected) {
t.Errorf(`Expected value to be %s, got %s`, expected, u)
}
})
}

188
2021/day8/main.go Normal file
View file

@ -0,0 +1,188 @@
package main
import (
"fmt"
"strings"
"github.com/jdkaplan/advent-of-code/aoc"
"github.com/jdkaplan/advent-of-code/aoc/stringset"
)
func main() {
lines := aoc.Input().ReadLines("day8.txt")
fmt.Println(part1(lines))
fmt.Println(part2(lines))
}
func part1(lines []string) int {
var count int
for _, l := range lines {
_, out := aoc.Cut(l, " | ")
for _, p := range strings.Fields(out) {
if _, ok := easy(p); ok {
count++
}
}
}
return count
}
func part2(lines []string) int {
var sum int
for _, l := range lines {
pats, out := aoc.Cut(l, " | ")
d := infer(strings.Fields(pats))
n, err := d.decode(strings.Fields(out))
if err != nil {
panic(err)
}
sum += n
}
return sum
}
func easy(segments string) (int, bool) {
switch len(segments) {
// 0 segments => {}
// 1 segments => {}
case 2:
return 1, true
case 3:
return 7, true
case 4:
return 4, true
case 7:
return 8, true
default:
// 5 segments => {2, 3, 5}
// 6 segments => {0, 6, 9}
return -1, false
}
}
// AA
// B C
// DD
// E F
// GG
type Display struct {
A, B, C, D, E, F, G string
}
func (d *Display) String() string {
return strings.Join([]string{d.A, d.B, d.C, d.D, d.E, d.F, d.G}, " ")
}
func infer(pats []string) *Display {
var one, four, seven, eight stringset.StringSet
var fives, sixes []stringset.StringSet
for _, pat := range pats {
s := stringset.Runes(pat)
switch len(s) {
case 2:
one = s
case 3:
seven = s
case 4:
four = s
case 5:
fives = append(fives, s)
case 6:
sixes = append(sixes, s)
case 7:
eight = s
default:
panic("WTF")
}
}
a := seven.Minus(one)
ifives := stringset.Intersect(fives...)
g := ifives.Minus(seven.Union(four))
d := ifives.Minus(a.Union(g))
e := eight.Minus(four.Union(a).Union(g))
five := stringset.Intersect(sixes...).Union(d)
six := five.Union(e)
c := eight.Minus(six)
// good
two := stringset.Union(a, c, d, e, g)
var three stringset.StringSet
for _, s := range fives {
if s.Equal(five) || s.Equal(two) {
continue
}
three = s
}
if len(three) == 0 {
panic("WAT")
}
f := three.Minus(two)
b := eight.Minus(stringset.Union(a, c, d, e, f, g))
return &Display{
A: a.Only(),
B: b.Only(),
C: c.Only(),
D: d.Only(),
E: e.Only(),
F: f.Only(),
G: g.Only(),
}
}
func (d *Display) decode(pats []string) (int, error) {
var n int
for _, p := range pats {
n *= 10
x, err := d.decodeSingle(p)
if err != nil {
return -1, err
}
n += x
}
return n, nil
}
func (d *Display) decodeSingle(pat string) (int, error) {
s := stringset.Runes(pat)
t0 := stringset.Slice([]string{d.A, d.B, d.C, d.E, d.F, d.G})
t1 := stringset.Slice([]string{d.C, d.F})
t2 := stringset.Slice([]string{d.A, d.C, d.D, d.E, d.G})
t3 := stringset.Slice([]string{d.A, d.C, d.D, d.F, d.G})
t4 := stringset.Slice([]string{d.B, d.C, d.D, d.F})
t5 := stringset.Slice([]string{d.A, d.B, d.D, d.F, d.G})
t6 := stringset.Slice([]string{d.A, d.B, d.D, d.E, d.F, d.G})
t7 := stringset.Slice([]string{d.A, d.C, d.F})
t8 := stringset.Slice([]string{d.A, d.B, d.C, d.D, d.E, d.F, d.G})
t9 := stringset.Slice([]string{d.A, d.B, d.C, d.D, d.F, d.G})
if s.Equal(t0) {
return 0, nil
}
if s.Equal(t1) {
return 1, nil
}
if s.Equal(t2) {
return 2, nil
}
if s.Equal(t3) {
return 3, nil
}
if s.Equal(t4) {
return 4, nil
}
if s.Equal(t5) {
return 5, nil
}
if s.Equal(t6) {
return 6, nil
}
if s.Equal(t7) {
return 7, nil
}
if s.Equal(t8) {
return 8, nil
}
if s.Equal(t9) {
return 9, nil
}
return -1, fmt.Errorf("decode segments: %s", s)
}