Compare commits

..

3 Commits

Author SHA1 Message Date
77fba87ef6 day25: complete solution sill need to finish day 21 for the last star!
Some checks failed
test / AoC 2024 (push) Failing after 2m15s
2024-12-24 21:35:05 -08:00
8b8ed2a323 day24: part 1 solution
Some checks failed
test / AoC 2024 (push) Failing after 4m8s
2024-12-23 21:37:50 -08:00
6efb9e0f83 day23: add pivot to bron-kerbosch, use more hashsets for speed
Some checks failed
test / AoC 2024 (push) Failing after 4m2s
2024-12-23 02:07:49 -08:00
7 changed files with 400 additions and 13 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -1,6 +1,6 @@
<!-- AOC TILES BEGIN -->
<h1 align="center">
2024 - 45 ⭐ - Rust
2024 - 48 ⭐ - Rust
</h1>
<a href="src/day1.rs">
<img src=".aoc_tiles/tiles/2024/01.png" width="161px">
@ -71,4 +71,10 @@
<a href="src/day23.rs">
<img src=".aoc_tiles/tiles/2024/23.png" width="161px">
</a>
<a href="src/day24.rs">
<img src=".aoc_tiles/tiles/2024/24.png" width="161px">
</a>
<a href="src/day25.rs">
<img src=".aoc_tiles/tiles/2024/25.png" width="161px">
</a>
<!-- AOC TILES END -->

View File

@ -20,7 +20,7 @@ impl Display for Node {
impl From<[char; 2]> for Node {
fn from(value: [char; 2]) -> Self {
Node(value)
Self(value)
}
}
@ -33,8 +33,8 @@ impl TryFrom<Vec<char>> for Node {
}
struct Network {
nodes: Vec<Node>,
edges: FxHashMap<Node, Vec<Node>>,
nodes: FxHashSet<Node>,
edges: FxHashMap<Node, FxHashSet<Node>>,
}
impl Network {
@ -56,6 +56,8 @@ impl Network {
}
sets
}
// Had to study Wikipedia for this one
// https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm
fn bron_kerbosch(
&self,
r: FxHashSet<Node>,
@ -65,8 +67,15 @@ impl Network {
let mut results = Vec::new();
if p.is_empty() && x.is_empty() {
return vec![r];
} else if p.is_empty() {
return Vec::new();
}
let p_iter = p.clone(); // so we can modify p
// choose the pivot with the most neighbours, to minimize the size of p_iter
let p_iter = if let Some(pivot) = p.union(&x).max_by(|a, b| self.edges[a].len().cmp(&self.edges[b].len())) {
FxHashSet::from_iter(p.difference(self.edges.get(pivot).unwrap()).copied())
} else {
p.clone()
};
for node in &p_iter {
let mut new_r = r.clone();
new_r.insert(*node);
@ -92,7 +101,7 @@ impl Network {
impl From<&str> for Network {
fn from(input: &str) -> Self {
let mut nodes = Vec::new();
let mut nodes = FxHashSet::default();
let mut edges = FxHashMap::default();
for line in input.lines() {
let (node1, node2) = line.split_once('-').unwrap();
@ -101,13 +110,13 @@ impl From<&str> for Network {
node2.chars().collect_vec().try_into().unwrap(),
);
if !nodes.contains(&node1) {
nodes.push(node1);
nodes.insert(node1);
}
if !nodes.contains(&node2) {
nodes.push(node2);
nodes.insert(node2);
}
edges.entry(node1).or_insert(Vec::new()).push(node2);
edges.entry(node2).or_insert(Vec::new()).push(node1);
edges.entry(node1).or_insert(FxHashSet::default()).insert(node2);
edges.entry(node2).or_insert(FxHashSet::default()).insert(node1);
}
Self { nodes, edges }
}
@ -120,10 +129,9 @@ fn parse(input: &str) -> Network {
#[aoc(day23, part1)]
pub fn part1(input: &str) -> i64 {
let network = parse(input);
// println!("edges: {:?}", network.edges);
let sets = network.groups_3();
let t_count = sets.iter().filter(|set| set.iter().any(|s| s.0[0] == 't')).count();
// println!("groups: {:?}", sets);
t_count as i64
}
@ -135,7 +143,6 @@ pub fn part2(input: &str) -> String {
let largest_set = best_sets.iter().max_by(|a, b| a.len().cmp(&b.len())).unwrap();
let mut largest = largest_set.iter().collect_vec();
largest.sort();
println!("best: {:?}", largest);
largest.iter().join(",")
}

229
src/day24.rs Normal file
View File

@ -0,0 +1,229 @@
use aoc_runner_derive::aoc;
use itertools::Itertools;
use nom::And;
use regex::Regex;
use rustc_hash::FxHashMap;
#[derive(Copy, Clone, Debug)]
enum Op {
And,
Or,
Xor,
Constant,
}
impl From<&str> for Op {
fn from(value: &str) -> Self {
match value {
"AND" => Self::And,
"OR" => Self::Or,
"XOR" => Self::Xor,
s => panic!("invalid operation {}", s),
}
}
}
#[derive(Clone, Debug)]
struct Gate {
op: Op,
value: Option<bool>,
arguments: [String; 2],
}
impl Gate {
fn eval(&self, machine: &GateMachine) -> bool {
match self.op {
Op::And => machine.val_of(&self.arguments[0]) && machine.val_of(&self.arguments[1]),
Op::Or => machine.val_of(&self.arguments[0]) || machine.val_of(&self.arguments[1]),
Op::Xor => machine.val_of(&self.arguments[0]) ^ machine.val_of(&self.arguments[1]),
Op::Constant => self.value.unwrap(),
}
}
}
#[derive(Debug)]
struct GateMachine {
gates: FxHashMap<String, Gate>,
}
impl GateMachine {
fn val_of(&self, gate: &str) -> bool {
println!("gate: {}", gate);
if let Some(val) = self.gates[gate].value {
val
} else {
self.gates[gate].eval(self)
}
}
}
fn parse(input: &str) -> GateMachine {
let mut gates = FxHashMap::default();
for line in input.lines() {
println!("{line}");
let const_re = Regex::new(r"^([xyz][0-9]{2}): ([01])$").unwrap();
let gate_re = Regex::new(r"^([a-z0-9]{3}) (AND|XOR|OR) ([a-z0-9]{3}) -> ([a-z0-9]{3})$").unwrap();
if let Some(caps) = const_re.captures(line) {
println!(" is const: {:?}", caps);
gates.insert(
caps[1].to_string(),
Gate {
op: Op::Constant,
value: if &caps[2] == "1" { Some(true) } else { Some(false) },
arguments: [String::new(), String::new()],
},
);
} else if let Some(caps) = gate_re.captures(line) {
println!(" is gate: {:?}", caps);
gates.insert(
caps[4].to_string(),
Gate {
op: Op::from(&caps[2]),
value: None,
arguments: [caps[1].to_string(), caps[3].to_string()],
},
);
}
}
GateMachine { gates }
}
#[aoc(day24, part1)]
pub fn part1(input: &str) -> i64 {
let machine = parse(input);
let z_gates = machine
.gates
.keys()
.filter(|k| k.starts_with('z'))
.map(|s| (s, s.split_at(1).1.parse::<usize>().unwrap()));
let bit_vals = z_gates
.map(|(name, bit)| if machine.val_of(name) { 1 << bit } else { 0 })
.fold(0, |accum, val| accum | val);
bit_vals
}
#[aoc(day24, part2)]
pub fn part2(input: &str) -> i64 {
0
}
#[cfg(test)]
mod tests {
use super::*;
const EXAMPLE1: &str = "x00: 1
x01: 1
x02: 1
y00: 0
y01: 1
y02: 0
x00 AND y00 -> z00
x01 XOR y01 -> z01x00: 1
x01: 0
x02: 1
x03: 1
x04: 0
y00: 1
y01: 1
y02: 1
y03: 1
y04: 1
ntg XOR fgs -> mjb
y02 OR x01 -> tnw
kwq OR kpj -> z05
x00 OR x03 -> fst
tgd XOR rvg -> z01
vdt OR tnw -> bfw
bfw AND frj -> z10
ffh OR nrd -> bqk
y00 AND y03 -> djm
y03 OR y00 -> psh
bqk OR frj -> z08
tnw OR fst -> frj
gnj AND tgd -> z11
bfw XOR mjb -> z00
x03 OR x00 -> vdt
gnj AND wpb -> z02
x04 AND y00 -> kjc
djm OR pbm -> qhw
nrd AND vdt -> hwm
kjc AND fst -> rvg
y04 OR y02 -> fgs
y01 AND x02 -> pbm
ntg OR kjc -> kwq
psh XOR fgs -> tgd
qhw XOR tgd -> z09
pbm OR djm -> kpj
x03 XOR y03 -> ffh
x00 XOR y04 -> ntg
bfw OR bqk -> z06
nrd XOR fgs -> wpb
frj XOR qhw -> z04
bqk OR frj -> z07
y03 OR x01 -> nrd
hwm AND bqk -> z03
tgd XOR rvg -> z12
tnw OR pbm -> gnj
x02 OR y02 -> z02";
const EXAMPLE2: &str = "x00: 1
x01: 0
x02: 1
x03: 1
x04: 0
y00: 1
y01: 1
y02: 1
y03: 1
y04: 1
ntg XOR fgs -> mjb
y02 OR x01 -> tnw
kwq OR kpj -> z05
x00 OR x03 -> fst
tgd XOR rvg -> z01
vdt OR tnw -> bfw
bfw AND frj -> z10
ffh OR nrd -> bqk
y00 AND y03 -> djm
y03 OR y00 -> psh
bqk OR frj -> z08
tnw OR fst -> frj
gnj AND tgd -> z11
bfw XOR mjb -> z00
x03 OR x00 -> vdt
gnj AND wpb -> z02
x04 AND y00 -> kjc
djm OR pbm -> qhw
nrd AND vdt -> hwm
kjc AND fst -> rvg
y04 OR y02 -> fgs
y01 AND x02 -> pbm
ntg OR kjc -> kwq
psh XOR fgs -> tgd
qhw XOR tgd -> z09
pbm OR djm -> kpj
x03 XOR y03 -> ffh
x00 XOR y04 -> ntg
bfw OR bqk -> z06
nrd XOR fgs -> wpb
frj XOR qhw -> z04
bqk OR frj -> z07
y03 OR x01 -> nrd
hwm AND bqk -> z03
tgd XOR rvg -> z12
tnw OR pbm -> gnj";
#[test]
fn part1_example() {
assert_eq!(part1(EXAMPLE1), 4);
assert_eq!(part1(EXAMPLE2), 2024);
}
#[test]
fn part2_example() {
assert_eq!(part2(EXAMPLE1), 0);
}
}

143
src/day25.rs Normal file
View File

@ -0,0 +1,143 @@
use aoc_runner_derive::aoc;
use itertools::Itertools;
enum LockKey {
Lock,
Key,
}
#[derive(Clone, Debug)]
struct LockPile {
keys: Vec<Vec<usize>>,
locks: Vec<Vec<usize>>,
}
fn parse_grid(lines: &Vec<&str>) -> (LockKey, Vec<usize>) {
assert_eq!(lines.len(), 7);
if lines[0].chars().all(|c| c == '#') {
// lock
let mut pins = vec![0; 5];
for row in 1..lines.len() {
let row_s = lines[row];
for i in 0..row_s.len() {
if row_s.chars().nth(i) == Some('#') {
pins[i] = row
}
}
}
(LockKey::Lock, pins)
} else if lines[6].chars().all(|c| c == '#') {
// key
let mut pins = vec![5; 5];
for row in (1..lines.len()).rev() {
let row_s = lines[row];
for i in 0..row_s.len() {
if row_s.chars().nth(i) == Some('#') {
pins[i] = 6 - row
}
}
}
(LockKey::Key, pins)
} else {
panic!("not a lock or a key: {:?}", lines);
}
}
fn parse(input: &str) -> LockPile {
let mut locks = Vec::new();
let mut keys = Vec::new();
let mut accum: Vec<&str> = Vec::new();
for line in input.lines() {
if line == "" {
let (lk, pins) = parse_grid(&accum);
match lk {
LockKey::Lock => locks.push(pins),
LockKey::Key => keys.push(pins),
}
accum.clear();
} else {
accum.push(line);
}
}
if accum.len() != 0 {
let (lk, pins) = parse_grid(&accum);
match lk {
LockKey::Lock => locks.push(pins),
LockKey::Key => keys.push(pins),
}
}
LockPile { keys, locks }
}
fn test_lock_key(lock: &Vec<usize>, key: &Vec<usize>) -> bool {
!lock.iter().zip(key.iter()).any(|(lp, kp)| lp + kp > 5)
}
#[aoc(day25, part1)]
pub fn part1(input: &str) -> i64 {
let lockpile = parse(input);
lockpile
.locks
.iter()
.cartesian_product(lockpile.keys.iter())
.filter(|(l, k)| test_lock_key(l, k))
.count() as i64
}
#[aoc(day25, part2)]
pub fn part2(_input: &str) -> String {
"run the other solutions for day 25 part 2!".to_string()
}
#[cfg(test)]
mod tests {
use super::*;
const EXAMPLE: &str = "#####
.####
.####
.####
.#.#.
.#...
.....
#####
##.##
.#.##
...##
...#.
...#.
.....
.....
#....
#....
#...#
#.#.#
#.###
#####
.....
.....
#.#..
###..
###.#
###.#
#####
.....
.....
.....
#....
#.#..
#.#.#
#####";
#[test]
fn part1_example() {
assert_eq!(part1(EXAMPLE), 3);
}
#[test]
fn part2_example() {}
}

View File

@ -14,6 +14,8 @@ pub mod day20;
pub mod day21;
pub mod day22;
pub mod day23;
pub mod day24;
pub mod day25;
pub mod day3;
pub mod day4;
pub mod day5;