2021 Day 8
This commit is contained in:
parent
0f454c5d1f
commit
2d203e541e
3 changed files with 336 additions and 0 deletions
108
2021/aoc/stringset/stringset.go
Normal file
108
2021/aoc/stringset/stringset.go
Normal 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
|
||||||
|
}
|
||||||
40
2021/aoc/stringset/stringset_test.go
Normal file
40
2021/aoc/stringset/stringset_test.go
Normal 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
188
2021/day8/main.go
Normal 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)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue