Day 23

prelude

https://adventofcode.com/2024/day/23

import { inputDay } from "./lib/utilities.js"
const parser = inp =>
  inp
    .trim()
    .split("\n")
    .map(x => x.split("-"))
const test = parser(
  "kh-tc\nqp-kh\nde-cg\nka-co\nyn-aq\nqp-ub\ncg-tb\nvc-aq\ntb-ka\nwh-tc\nyn-cg\nkh-ub\nta-co\nde-co\ntc-td\ntb-wq\nwh-td\nta-ka\ntd-qp\naq-cg\nwq-ub\nub-vc\nde-ta\nwq-aq\nwq-vc\nwh-yn\nka-de\nkh-ta\nco-tc\nwh-qp\ntb-vc\ntd-yn\n",
)
display(test)

const input = await inputDay(23, { parser })
display(input)

part 1

// gather the network into a map of node -> set of connections
function gather(network) {
  const m = new Map()
  for (const [a, b] of network) {
    if (!m.get(a)) m.set(a, new Set())
    if (!m.get(b)) m.set(b, new Set())
    m.get(a).add(b)
    m.get(b).add(a)
  }
  return m
}
display(gather(test))
// find and return connections a -> b -> c -> a
function run(network) {
  const net = gather(network)
  const triples = new Set()
  for (const key of net.keys().filter(x => x.startsWith("t"))) {
    for (const conn of net.get(key)) {
      for (const conn_ of net.get(conn)) {
        if (conn_ == key || conn_ == conn) continue
        if (net.get(conn_)?.has(key))
          triples.add([key, conn, conn_].sort().join("->"))
      }
    }
  }
  return triples
}

display(run(test))
display(run(input))

part 2

A function to get the complete subgroups for a given key:

function subgroup(network, key) {
  // start with the pairs of each node with its connections
  let groups = [
    ...network
      .get(key)
      .values()
      .map(c => [key, c]),
  ]
  let found = true
  while (found) {
    // quit the loop when we haven't added any connections
    found = false
    for (const group of groups) {
      // check the last node of every group
      let node = group[group.length - 1]

      // for each of its connections
      for (const conn of network.get(node)) {
        // if the node has a connection to every item in the group, add it
        if (group.every(n => network.get(n).has(conn))) {
          group.push(conn)
          found = true
        }
      }
    }
  }
  return groups
}
display(subgroup(gather(test), "co"))

Now we can iterate each node, get the largest subgroup for it, and return the largest of these

function largestSubgroup(network) {
  const net = gather(network)
  let max = ""
  for (const key of net.keys()) {
    const maxSubgroup = subgroup(net, key)
      .sort(x => -x.length)[0]
      .sort()
      .join(",")
    if (maxSubgroup.length > max.length) {
      max = maxSubgroup
    }
  }
  return max
}

display(largestSubgroup(test))
display(largestSubgroup(input))