adventofcode/2024/day23/main.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
}