diff --git a/2023/day25/main.go b/2023/day25/main.go index 7146225..6c1f430 100644 --- a/2023/day25/main.go +++ b/2023/day25/main.go @@ -2,21 +2,186 @@ 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() { - inp := h.StdinToStringSlice() - part1(inp) - fmt.Println() - part2(inp) + 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 part1(inp []string) { - fmt.Println("# Part 1") +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 part2(inp []string) { - fmt.Println("# Part 2") +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 } diff --git a/2023/day25/testinput b/2023/day25/testinput new file mode 100644 index 0000000..bbfda0b --- /dev/null +++ b/2023/day25/testinput @@ -0,0 +1,13 @@ +jqt: rhn xhk nvd +rsh: frs pzl lsr +xhk: hfx +cmg: qnr nvd lhk bvb +rhn: xhk bvb hfx +bvb: xhk hfx +pzl: lsr hfx nvd +qnr: nvd +ntq: jqt hfx bvb xhk +nvd: lhk +lsr: lhk +rzs: qnr cmg lsr rsh +frs: qnr lhk lsr diff --git a/helpers/queue.go b/helpers/queue.go index 0b12f27..715dbfa 100644 --- a/helpers/queue.go +++ b/helpers/queue.go @@ -1,5 +1,7 @@ package aoc +import "fmt" + type Queue[T any] struct { items []T } @@ -80,3 +82,11 @@ func (q *Queue[T]) Insert(item T, location int) { func (q *Queue[T]) InsertVector(items []T, location int) { q.items = append(q.items[:location], append(items, q.items[location:]...)...) } + +func (q Queue[T]) String() string { + res := "[ " + for i := range q.items { + res = fmt.Sprintf("%s %v", res, q.items[i]) + } + return fmt.Sprintf("%s ]", res) +}