Skip to content

Commit e66e734

Browse files
committed
Add, complete day 12.
I'm pretty OK with this one; finding the graph groups of 2000 lines of connection input in less than half a second seems like a decent job to me. I hadn't expected to need to factor out map_connections_generic for part 2, but I can't complain about the result; I kind of like how it came out. I do wish that closures implemented Copy/Clone appropriately, though: rust-lang/rfcs#2132 $ cargo build --release && time target/release/day12 && wc -l input.txt Compiling day12 v0.1.0 (file:///mnt/d/Users/coriolinus/Documents/Projects/adventofcode.com/adventofcode-2017/day12) Finished release [optimized] target(s) in 4.92 secs real 0m0.397s user 0m0.375s sys 0m0.000s 2000 input.txt
1 parent 4f45ed2 commit e66e734

File tree

6 files changed

+146
-0
lines changed

6 files changed

+146
-0
lines changed

day12/Cargo.toml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "day12"
3+
version = "0.1.0"
4+
authors = ["Peter Goodspeed-Niklaus <[email protected]>"]
5+
build = "build.rs" # LALRPOP preprocessing
6+
7+
[dependencies]
8+
util = { path = "../util" }
9+
lalrpop-util = "0.13.1"
10+
regex = "0.2.0"
11+
12+
[build-dependencies]
13+
lalrpop = "0.13.1"

day12/build.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
extern crate lalrpop;
2+
3+
fn main() {
4+
lalrpop::process_root().unwrap();
5+
}

day12/src/.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# parser.rs is generated at compile time by parser.lalrpop,
2+
# so is not necessary.
3+
# Ideally, we could express the rule "<n:*>.rs iff <n>.lalrpop",
4+
# but if that's possible in a standard .gitignore file,
5+
# I don't know how to do it.
6+
parser.rs

day12/src/lib.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
pub mod parser;
2+
pub use parser::parse_connections;
3+
4+
type Connections = (usize, Vec<usize>);
5+
type Graph = Vec<bool>;
6+
7+
fn map_connections(graph: &mut Graph, connections: &[Connections], node: usize) {
8+
map_connections_generic(graph, connections, node, true, &|b: bool| !b);
9+
}
10+
11+
fn map_connections_node(
12+
graph: &mut Vec<Node>,
13+
connections: &[Connections],
14+
node: usize,
15+
group: usize,
16+
) {
17+
map_connections_generic(graph, connections, node, Some(group), &|n| n.is_none());
18+
}
19+
20+
fn map_connections_generic<N, IsFalse>(
21+
graph: &mut Vec<N>,
22+
connections: &[Connections],
23+
node: usize,
24+
true_value: N,
25+
is_false: &IsFalse,
26+
) where
27+
N: Copy,
28+
IsFalse: Fn(N) -> bool,
29+
{
30+
graph[node] = true_value;
31+
for &connection in connections[node].1.iter() {
32+
if is_false(graph[connection]) {
33+
map_connections_generic(graph, connections, connection, true_value, is_false);
34+
}
35+
}
36+
}
37+
38+
pub fn connected_to_zero(connections: &[Connections]) -> Vec<usize> {
39+
assert!(
40+
connections.iter().enumerate().all(
41+
|(idx, cxn)| idx == cxn.0,
42+
),
43+
"Input connections must be sorted by node index"
44+
);
45+
let mut graph = vec![false; connections.len()];
46+
if graph.len() > 0 {
47+
map_connections(&mut graph, connections, 0)
48+
}
49+
graph
50+
.iter()
51+
.enumerate()
52+
.filter(|&(_, node)| *node)
53+
.map(|(index, _)| index)
54+
.collect()
55+
}
56+
57+
type Node = Option<usize>;
58+
59+
pub fn count_groups(connections: &[Connections]) -> usize {
60+
assert!(
61+
connections.iter().enumerate().all(
62+
|(idx, cxn)| idx == cxn.0,
63+
),
64+
"Input connections must be sorted by node index"
65+
);
66+
let mut graph: Vec<Node> = vec![None; connections.len()];
67+
let mut group = 0;
68+
69+
while let Some(idx) = graph
70+
.iter()
71+
.enumerate()
72+
.filter(|&(_, g)| g.is_none())
73+
.map(|(idx, _)| idx)
74+
.next()
75+
{
76+
group += 1;
77+
map_connections_node(&mut graph, connections, idx, group);
78+
}
79+
80+
group
81+
}

day12/src/main.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
extern crate day12;
2+
use day12::{parse_connections, connected_to_zero, count_groups};
3+
4+
extern crate util;
5+
use util::read_file;
6+
7+
fn main() {
8+
let input = read_file("input.txt");
9+
let connections = input
10+
.trim()
11+
.lines()
12+
.map(|line| parse_connections(line).expect("Parse error"))
13+
.collect::<Vec<_>>();
14+
let zero_graph = connected_to_zero(&connections);
15+
println!("# nodes connected to 0: {}", zero_graph.len());
16+
println!("# groups: {}", count_groups(&connections));
17+
}

day12/src/parser.lalrpop

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use std::str::FromStr;
2+
3+
grammar;
4+
5+
pub connections: (usize, Vec<usize>) = {
6+
<id:NodeId> "<->" <l:NodeIdList> => (id, l),
7+
};
8+
9+
NodeId: usize = {
10+
r"\d+" => usize::from_str(<>).unwrap(),
11+
};
12+
13+
NodeIdList = CommaSeparatedList<NodeId>;
14+
15+
CommaSeparatedList<T>: Vec<T> = { // (1)
16+
<v:(<T> ",")*> <e:T?> => match e { // (2)
17+
None => v,
18+
Some(e) => {
19+
let mut v = v;
20+
v.push(e);
21+
v
22+
}
23+
}
24+
};

0 commit comments

Comments
 (0)