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