adventofcode/2023/day25/main.go

188 lines
4.0 KiB
Go

package main
import (
"fmt"
"math/rand"
"strings"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
type component struct {
id string
connections map[string]component
conns map[string]bool
}
type components struct {
all map[string]component
edges []connections
}
type connections struct {
from, to string
track []string
}
func main() {
lines := h.StdinToStringSlice()
var comps components
var edges []connections
var counter int
var tries int
for tries < 100 {
counter = 0
for {
counter++
compsS := components{all: make(map[string]component)}
for _, line := range lines {
connect(&compsS, line)
}
cc := minCut(compsS)
cuts := len(cc.edges)
edges = cc.edges
comps = compsS
if cuts == 3 {
break
}
if counter >= 100 {
break
}
}
tries++
if counter < 100 {
break
}
}
total := cutInGroups(&comps, edges)
fmt.Println(total)
}
func connect(comps *components, line string) {
split := strings.Split(line, ": ")
nodeName := split[0]
connectionsNames := strings.Split(split[1], " ")
node := component{id: nodeName, connections: make(map[string]component), conns: make(map[string]bool)}
if _, ok := comps.all[nodeName]; !ok {
comps.all[nodeName] = node
}
for _, neighborName := range connectionsNames {
neighbor := component{id: neighborName, connections: make(map[string]component), conns: make(map[string]bool)}
if _, ok := comps.all[neighborName]; !ok {
comps.all[neighborName] = neighbor
}
comps.all[nodeName].connections[neighborName] = neighbor
comps.all[nodeName].conns[neighborName] = true
comps.all[neighborName].connections[nodeName] = node
comps.all[neighborName].conns[nodeName] = true
comps.edges = append(comps.edges, connections{
from: nodeName,
to: neighborName,
track: []string{nodeName, neighborName},
})
}
}
func minCut(comps components) components {
cc := components{
all: make(map[string]component),
edges: comps.edges,
}
for key, val := range comps.all {
cc.all[key] = val
}
for len(cc.all) > 2 {
u, v := randomComps(&cc)
contract(&cc, u, v)
}
return cc
}
func randomComps(comps *components) (component, component) {
randomN := rand.Intn(len(comps.edges))
edge := comps.edges[randomN]
comps.edges = append(comps.edges[:randomN], comps.edges[randomN+1:]...)
randCorn := rand.Intn(2)
var nameU, nameV string
if randCorn == 0 {
nameU = edge.from
nameV = edge.to
} else {
nameU = edge.to
nameV = edge.from
}
u, v := comps.all[nameU], comps.all[nameV]
return u, v
}
func contract(comps *components, u component, v component) {
delete(comps.all, v.id)
delete(comps.all[u.id].connections, v.id)
for _, node := range comps.all {
if _, ok := node.connections[v.id]; ok {
delete(node.connections, v.id)
node.connections[u.id] = u
comps.all[u.id].connections[node.id] = node
}
}
for i := 0; i < len(comps.edges); {
if comps.edges[i].from == v.id {
comps.edges[i].from = u.id
}
if comps.edges[i].to == v.id {
comps.edges[i].to = u.id
}
if comps.edges[i].from == comps.edges[i].to {
comps.edges = append(comps.edges[:i], comps.edges[i+1:]...)
continue
}
i++
}
}
func cutInGroups(comps *components, edges []connections) int {
var final1, final2 string
for i, edge := range edges {
cut1 := edge.track[0]
cut2 := edge.track[1]
delete(comps.all[cut1].conns, cut2)
delete(comps.all[cut2].conns, cut1)
if i == len(edges)-1 {
final1 = cut1
final2 = cut2
}
}
group1 := countN(comps, final1)
group2 := countN(comps, final2)
return group1 * group2
}
func countN(comps *components, start string) int {
visited := make(map[string]bool)
counter := 0
entry := start
path := []string{entry}
for len(path) > 0 {
currentPath := path[0]
path = path[1:]
if _, ok := visited[currentPath]; ok {
continue
}
counter++
currentcomponent := comps.all[currentPath]
visited[currentPath] = true
for key := range currentcomponent.conns {
if _, ok := visited[key]; ok {
continue
}
path = append(path, key)
}
}
return counter
}