180 lines
3.5 KiB
Go
180 lines
3.5 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"sort"
|
||
|
"strings"
|
||
|
|
||
|
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
inp := h.StdinToStringSlice()
|
||
|
part1(inp)
|
||
|
fmt.Println()
|
||
|
part2(inp)
|
||
|
}
|
||
|
|
||
|
func parseInput(inp []string) NetworkMap {
|
||
|
network := make(map[string]h.Set)
|
||
|
for _, l := range inp {
|
||
|
pts := strings.Split(l, "-")
|
||
|
if _, ok := network[pts[0]]; !ok {
|
||
|
network[pts[0]] = h.NewSet(1)
|
||
|
}
|
||
|
if _, ok := network[pts[1]]; !ok {
|
||
|
network[pts[1]] = h.NewSet(1)
|
||
|
}
|
||
|
network[pts[0]].Add(pts[1])
|
||
|
network[pts[1]].Add(pts[0])
|
||
|
}
|
||
|
return NewNetworkMap(network)
|
||
|
}
|
||
|
|
||
|
func part1(inp []string) {
|
||
|
nwk := parseInput(inp)
|
||
|
fmt.Println("# Part 1")
|
||
|
total := 0
|
||
|
wrk := nwk
|
||
|
for _, comp := range wrk.Comps {
|
||
|
conns := make([]string, 0, len(comp.Conns))
|
||
|
for name := range comp.Conns {
|
||
|
conns = append(conns, name)
|
||
|
total++
|
||
|
}
|
||
|
}
|
||
|
tris := wrk.FindTGroups()
|
||
|
fmt.Println(len(tris))
|
||
|
}
|
||
|
|
||
|
func part2(inp []string) {
|
||
|
nwk := parseInput(inp)
|
||
|
cnt := len(nwk.Comps)
|
||
|
wrk := h.NewSet(cnt)
|
||
|
for nm := range nwk.Comps {
|
||
|
wrk.Add(nm)
|
||
|
}
|
||
|
group := BronKerbosch(h.NewSet(cnt+1), wrk, h.NewSet(cnt+1), h.NewSet(0), nwk.Network)
|
||
|
fmt.Println("Group Size:", group.Size())
|
||
|
fmt.Println("Password:", strings.Join(group.ToSortedSlice(), ","))
|
||
|
}
|
||
|
|
||
|
func BronKerbosch(cl, chk, excl, kmax h.Set, graph map[string]h.Set) h.Set {
|
||
|
if chk.Empty() && excl.Empty() {
|
||
|
return cl
|
||
|
}
|
||
|
for vx := range chk {
|
||
|
vxSet := h.NewSetFromValues(vx)
|
||
|
vxConns := graph[vx]
|
||
|
nC := cl.Union(vxSet)
|
||
|
nWrk := chk.Intersection(vxConns)
|
||
|
if nC.Size()+nWrk.Size() <= kmax.Size() {
|
||
|
continue
|
||
|
}
|
||
|
newExcl := excl.Intersection(vxConns)
|
||
|
fC := BronKerbosch(nC, nWrk, newExcl, kmax, graph)
|
||
|
if fC.Size() > kmax.Size() {
|
||
|
kmax = fC
|
||
|
}
|
||
|
chk.Remove(vx)
|
||
|
excl.Add(vx)
|
||
|
}
|
||
|
return kmax
|
||
|
}
|
||
|
|
||
|
type NetworkMap struct {
|
||
|
Network map[string]h.Set
|
||
|
Comps map[string]*Comp
|
||
|
}
|
||
|
|
||
|
func NewNetworkMap(nwk map[string]h.Set) NetworkMap {
|
||
|
nm := NetworkMap{Network: nwk, Comps: make(map[string]*Comp)}
|
||
|
for name, conns := range nwk {
|
||
|
for conn := range conns {
|
||
|
nm.Connect(name, conn)
|
||
|
}
|
||
|
}
|
||
|
return nm
|
||
|
}
|
||
|
|
||
|
func (n *NetworkMap) Connect(c1, c2 string) {
|
||
|
n.GetComputer(c1).Connect(n.GetComputer(c2))
|
||
|
}
|
||
|
|
||
|
func (n *NetworkMap) GetComputer(nm string) *Comp {
|
||
|
if cm, ok := n.Comps[nm]; ok {
|
||
|
return cm
|
||
|
}
|
||
|
cm := NewComp(nm)
|
||
|
n.Comps[nm] = cm
|
||
|
return cm
|
||
|
}
|
||
|
|
||
|
func (n *NetworkMap) FindTGroups() map[Group]bool {
|
||
|
ret := make(map[Group]bool)
|
||
|
chk := make(map[string]bool)
|
||
|
for cn, comp := range n.Comps {
|
||
|
if chk[cn] {
|
||
|
continue
|
||
|
}
|
||
|
for nn, next := range comp.Conns {
|
||
|
friends := comp.FindFriends(next)
|
||
|
for _, friend := range friends {
|
||
|
if cn[0] == 't' || nn[0] == 't' || friend[0] == 't' {
|
||
|
grp := NewGroup([3]string{cn, nn, friend})
|
||
|
ret[grp] = true
|
||
|
}
|
||
|
}
|
||
|
chk[cn] = true
|
||
|
}
|
||
|
}
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
type Group struct {
|
||
|
Comps [3]string
|
||
|
}
|
||
|
|
||
|
func NewGroup(nodes [3]string) Group {
|
||
|
s := nodes[:]
|
||
|
sort.Strings(s)
|
||
|
return Group{Comps: [3]string{s[0], s[1], s[2]}}
|
||
|
}
|
||
|
|
||
|
func (g Group) String() string {
|
||
|
return fmt.Sprintf("%s-%s-%s", g.Comps[0], g.Comps[1], g.Comps[2])
|
||
|
}
|
||
|
|
||
|
type Comp struct {
|
||
|
Name string
|
||
|
Conns map[string]*Comp
|
||
|
}
|
||
|
|
||
|
func NewComp(n string) *Comp {
|
||
|
return &Comp{Name: n, Conns: make(map[string]*Comp)}
|
||
|
}
|
||
|
|
||
|
func (c *Comp) Connect(o *Comp) {
|
||
|
c.Conns[o.Name] = o
|
||
|
o.Conns[c.Name] = c
|
||
|
}
|
||
|
|
||
|
func (c *Comp) FindFriends(o *Comp) []string {
|
||
|
var friends []string
|
||
|
checked := make(map[string]bool)
|
||
|
for name := range c.Conns {
|
||
|
if name == c.Name || name == o.Name {
|
||
|
continue
|
||
|
}
|
||
|
if _, ok := checked[name]; ok {
|
||
|
continue
|
||
|
}
|
||
|
if _, ok := o.Conns[name]; !ok {
|
||
|
continue
|
||
|
}
|
||
|
friends = append(friends, name)
|
||
|
checked[name] = true
|
||
|
}
|
||
|
return friends
|
||
|
}
|