Haskell
The solution for part two could now be used for part one as well but then I would have to rewrite part 1 .-.
import Control.Arrow
import Data.Ord (comparing)
import qualified Data.List as List
import qualified Data.Map as Map
import qualified Data.Set as Set
parse = Map.fromListWith Set.union . List.map (second Set.singleton) . uncurry (++) . (id &&& List.map (uncurry (flip (,)))) . map (break (== '-') >>> second (drop 1)) . takeWhile (/= "") . lines
depthSearch connections ps
| length ps == 4 && head ps == last ps = [ps]
| length ps == 4 = []
| otherwise = head
>>> (connections Map.!)
>>> Set.toList
>>> List.map (:ps)
>>> List.concatMap (depthSearch connections)
$ ps
interconnections (computer, connections) = depthSearch connections [computer]
part1 = (Map.assocs &&& repeat)
>>> first (List.map (uncurry Set.insert))
>>> first (Set.toList . Set.unions)
>>> uncurry zip
>>> List.concatMap interconnections
>>> List.map (Set.fromList . take 3)
>>> List.filter (Set.fold (List.head >>> (== 't') >>> (||)) False)
>>> Set.fromList
>>> Set.size
getLANParty computer connections = (connections Map.!)
>>> findLanPartyComponent connections [computer]
$ computer
filterCandidates connections participants candidates = List.map (connections Map.!)
>>> List.foldl Set.intersection candidates
>>> Set.filter ((connections Map.!) >>> \ s -> List.all (flip Set.member s) participants)
$ participants
findLanPartyComponent connections participants candidates
| Set.null validParticipants = participants
| otherwise = findLanPartyComponent connections (nextParticipant : participants) (Set.delete nextParticipant candidates)
where
nextParticipant = Set.findMin validParticipants
validParticipants = filterCandidates connections participants candidates
part2 = (Map.keys &&& repeat)
>>> uncurry zip
>>> List.map ((uncurry getLANParty) >>> List.sort)
>>> List.nub
>>> List.maximumBy (comparing List.length)
>>> List.intercalate ","
main = getContents
>>= print
. (part1 &&& part2)
. parse