Add 2024 Day 23
This commit is contained in:
parent
c99638f28d
commit
7520d4956b
5 changed files with 3655 additions and 0 deletions
13
2024/day23_lan_party/Cargo.toml
Normal file
13
2024/day23_lan_party/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "day23_lan_party"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
# criterion = "0.5.1"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "test_benchmark"
|
||||||
|
harness = false
|
109
2024/day23_lan_party/challenge.md
Normal file
109
2024/day23_lan_party/challenge.md
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
As The Historians wander around a secure area at Easter Bunny HQ, you come across posters for a [LAN party](https://en.wikipedia.org/wiki/LAN_party) scheduled for today! Maybe you can find it; you connect to a nearby [datalink port](/2016/day/9) and download a map of the local network (your puzzle input).
|
||||||
|
|
||||||
|
The network map provides a list of every *connection between two computers*. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
kh-tc
|
||||||
|
qp-kh
|
||||||
|
de-cg
|
||||||
|
ka-co
|
||||||
|
yn-aq
|
||||||
|
qp-ub
|
||||||
|
cg-tb
|
||||||
|
vc-aq
|
||||||
|
tb-ka
|
||||||
|
wh-tc
|
||||||
|
yn-cg
|
||||||
|
kh-ub
|
||||||
|
ta-co
|
||||||
|
de-co
|
||||||
|
tc-td
|
||||||
|
tb-wq
|
||||||
|
wh-td
|
||||||
|
ta-ka
|
||||||
|
td-qp
|
||||||
|
aq-cg
|
||||||
|
wq-ub
|
||||||
|
ub-vc
|
||||||
|
de-ta
|
||||||
|
wq-aq
|
||||||
|
wq-vc
|
||||||
|
wh-yn
|
||||||
|
ka-de
|
||||||
|
kh-ta
|
||||||
|
co-tc
|
||||||
|
wh-qp
|
||||||
|
tb-vc
|
||||||
|
td-yn
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Each line of text in the network map represents a single connection; the line `kh-tc` represents a connection between the computer named `kh` and the computer named `tc`. Connections aren't directional; `tc-kh` would mean exactly the same thing.
|
||||||
|
|
||||||
|
LAN parties typically involve multiplayer games, so maybe you can locate it by finding groups of connected computers. Start by looking for *sets of three computers* where each computer in the set is connected to the other two computers.
|
||||||
|
|
||||||
|
In this example, there are `12` such sets of three inter-connected computers:
|
||||||
|
|
||||||
|
```
|
||||||
|
aq,cg,yn
|
||||||
|
aq,vc,wq
|
||||||
|
co,de,ka
|
||||||
|
co,de,ta
|
||||||
|
co,ka,ta
|
||||||
|
de,ka,ta
|
||||||
|
kh,qp,ub
|
||||||
|
qp,td,wh
|
||||||
|
tb,vc,wq
|
||||||
|
tc,td,wh
|
||||||
|
td,wh,yn
|
||||||
|
ub,vc,wq
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
If the Chief Historian is here, *and* he's at the LAN party, it would be best to know that right away. You're pretty sure his computer's name starts with `t`, so consider only sets of three computers where at least one computer's name starts with `t`. That narrows the list down to `*7*` sets of three inter-connected computers:
|
||||||
|
|
||||||
|
```
|
||||||
|
co,de,ta
|
||||||
|
co,ka,ta
|
||||||
|
de,ka,ta
|
||||||
|
qp,td,wh
|
||||||
|
tb,vc,wq
|
||||||
|
tc,td,wh
|
||||||
|
td,wh,yn
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Find all the sets of three inter-connected computers. *How many contain at least one computer with a name that starts with `t`?*
|
||||||
|
|
||||||
|
Your puzzle answer was `1304`.
|
||||||
|
|
||||||
|
\--- Part Two ---
|
||||||
|
----------
|
||||||
|
|
||||||
|
There are still way too many results to go through them all. You'll have to find the LAN party another way and go there yourself.
|
||||||
|
|
||||||
|
Since it doesn't seem like any employees are around, you figure they must all be at the LAN party. If that's true, the LAN party will be the *largest set of computers that are all connected to each other*. That is, for each computer at the LAN party, that computer will have a connection to every other computer at the LAN party.
|
||||||
|
|
||||||
|
In the above example, the largest set of computers that are all connected to each other is made up of `co`, `de`, `ka`, and `ta`. Each computer in this set has a connection to every other computer in the set:
|
||||||
|
|
||||||
|
```
|
||||||
|
ka-co
|
||||||
|
ta-co
|
||||||
|
de-co
|
||||||
|
ta-ka
|
||||||
|
de-ta
|
||||||
|
ka-de
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
The LAN party posters say that the *password* to get into the LAN party is the name of every computer at the LAN party, sorted alphabetically, then joined together with commas. (The people running the LAN party are clearly a bunch of nerds.) In this example, the password would be `*co,de,ka,ta*`.
|
||||||
|
|
||||||
|
*What is the password to get into the LAN party?*
|
||||||
|
|
||||||
|
Your puzzle answer was `ao,es,fe,if,in,io,ky,qq,rd,rn,rv,vc,vl`.
|
||||||
|
|
||||||
|
Both parts of this puzzle are complete! They provide two gold stars: \*\*
|
||||||
|
|
||||||
|
At this point, you should [return to your Advent calendar](/2024) and try another puzzle.
|
||||||
|
|
||||||
|
If you still want to see it, you can [get your puzzle input](23/input).
|
121
2024/day23_lan_party/src/lib.rs
Normal file
121
2024/day23_lan_party/src/lib.rs
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
use core::fmt::Display;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum ParseError<'a> {
|
||||||
|
ComputerName(&'a str),
|
||||||
|
LineMalformed(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ParseError<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::ComputerName(e) => write!(f, "Computer name doesn't consist of two ascii characters: \"{e}\"."),
|
||||||
|
Self::LineMalformed(v) => write!(f, "Line is malformed: {v}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Connection = u32;
|
||||||
|
|
||||||
|
fn try_connection_from(value: &str) -> Result<Connection, ParseError> {
|
||||||
|
if let Some((lhs, rhs)) = value.split_once('-') {
|
||||||
|
if lhs.len() != 2 {
|
||||||
|
return Err(ParseError::ComputerName(lhs));
|
||||||
|
}
|
||||||
|
let lhs_bytes = lhs.as_bytes();
|
||||||
|
let lhs = ((lhs_bytes[0] as u32) << 8) | (lhs_bytes[1] as u32);
|
||||||
|
if rhs.len() != 2 {
|
||||||
|
return Err(ParseError::ComputerName(rhs));
|
||||||
|
}
|
||||||
|
let rhs_bytes = rhs.as_bytes();
|
||||||
|
let rhs = ((rhs_bytes[0] as u32) << 8) | rhs_bytes[1] as u32;
|
||||||
|
|
||||||
|
Ok((lhs.min(rhs) << 16) | (lhs.max(rhs)))
|
||||||
|
} else {
|
||||||
|
Err(ParseError::LineMalformed(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn triples(conns: &HashSet<Connection>) -> Vec<(u16, u16, u16)> {
|
||||||
|
let mut res = Vec::new();
|
||||||
|
conns.iter().for_each(|conn| {
|
||||||
|
let (lhs, rhs) = ((conn >> 16) as u16, (conn & 0xFFFF) as u16);
|
||||||
|
conns.iter().filter(|&&other| (other >> 16) as u16 == lhs).for_each(|new| {
|
||||||
|
let new = new & 0xFFFF;
|
||||||
|
if conns.contains(&(((rhs as u32) << 16) | new)) {
|
||||||
|
res.push((lhs, rhs, new as u16));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn largest_clique(triples: &[(u16, u16, u16)], conns: &HashSet<u32>) -> Vec<u16> {
|
||||||
|
let mut cliques: Vec<_> = triples.iter().map(|(l, m, r)| vec![*l, *m, *r]).collect();
|
||||||
|
let mut next_cliques = Vec::with_capacity(cliques.len());
|
||||||
|
while cliques.len() > 1 {
|
||||||
|
for clique in cliques.iter() {
|
||||||
|
conns.iter()
|
||||||
|
.filter(|&&conn| (conn >> 16) as u16 == clique[0])
|
||||||
|
.for_each(|&conn| {
|
||||||
|
let new = (conn & 0xFFFF) as u16;
|
||||||
|
if clique.iter().skip(1).all(|old| conns.contains(&((*old as u32) << 16 | new as u32))) {
|
||||||
|
let mut new_clique = clique.to_vec();
|
||||||
|
new_clique.push(new);
|
||||||
|
next_cliques.push(new_clique);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
std::mem::swap(&mut cliques, &mut next_cliques);
|
||||||
|
next_cliques.clear();
|
||||||
|
}
|
||||||
|
cliques[0].to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn password(computers: &[u16]) -> String {
|
||||||
|
let computers = computers.to_vec();
|
||||||
|
computers
|
||||||
|
.iter()
|
||||||
|
.map(|n| String::from_utf8(vec![(n >> 8) as u8, (n & 0xFF) as u8]).unwrap())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(",")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(input: &str) -> Result<(usize, String), ParseError> {
|
||||||
|
const FIRST_LETTER_T: u16 = (b't' as u16) << 8;
|
||||||
|
let conns: HashSet<_> = input.lines().map(try_connection_from).collect::<Result<HashSet<_>, _>>()?;
|
||||||
|
let triples = triples(&conns);
|
||||||
|
let first = triples
|
||||||
|
.iter()
|
||||||
|
.filter(|(l, m, r)|
|
||||||
|
l & 0xFF00 == FIRST_LETTER_T ||
|
||||||
|
m & 0xFF00 == FIRST_LETTER_T ||
|
||||||
|
r & 0xFF00 == FIRST_LETTER_T)
|
||||||
|
.count();
|
||||||
|
let party = largest_clique(&triples, &conns);
|
||||||
|
let second = password(&party);
|
||||||
|
Ok((first, second))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
|
fn read_file(name: &str) -> String {
|
||||||
|
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sample() {
|
||||||
|
let sample_input = read_file("tests/sample_input");
|
||||||
|
assert_eq!(run(&sample_input), Ok((7, "co,de,ka,ta".to_string())));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_challenge() {
|
||||||
|
let challenge_input = read_file("tests/challenge_input");
|
||||||
|
assert_eq!(run(&challenge_input), Ok((1304, "ao,es,fe,if,in,io,ky,qq,rd,rn,rv,vc,vl".to_string())));
|
||||||
|
}
|
||||||
|
}
|
3380
2024/day23_lan_party/tests/challenge_input
Normal file
3380
2024/day23_lan_party/tests/challenge_input
Normal file
File diff suppressed because it is too large
Load diff
32
2024/day23_lan_party/tests/sample_input
Normal file
32
2024/day23_lan_party/tests/sample_input
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
kh-tc
|
||||||
|
qp-kh
|
||||||
|
de-cg
|
||||||
|
ka-co
|
||||||
|
yn-aq
|
||||||
|
qp-ub
|
||||||
|
cg-tb
|
||||||
|
vc-aq
|
||||||
|
tb-ka
|
||||||
|
wh-tc
|
||||||
|
yn-cg
|
||||||
|
kh-ub
|
||||||
|
ta-co
|
||||||
|
de-co
|
||||||
|
tc-td
|
||||||
|
tb-wq
|
||||||
|
wh-td
|
||||||
|
ta-ka
|
||||||
|
td-qp
|
||||||
|
aq-cg
|
||||||
|
wq-ub
|
||||||
|
ub-vc
|
||||||
|
de-ta
|
||||||
|
wq-aq
|
||||||
|
wq-vc
|
||||||
|
wh-yn
|
||||||
|
ka-de
|
||||||
|
kh-ta
|
||||||
|
co-tc
|
||||||
|
wh-qp
|
||||||
|
tb-vc
|
||||||
|
td-yn
|
Loading…
Add table
Add a link
Reference in a new issue