Skip to content

Commit

Permalink
Add, complete day 12.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
coriolinus committed Dec 12, 2017
1 parent 4f45ed2 commit e66e734
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 0 deletions.
13 changes: 13 additions & 0 deletions day12/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "day12"
version = "0.1.0"
authors = ["Peter Goodspeed-Niklaus <peter.r.goodspeedniklaus@gmail.com>"]
build = "build.rs" # LALRPOP preprocessing

[dependencies]
util = { path = "../util" }
lalrpop-util = "0.13.1"
regex = "0.2.0"

[build-dependencies]
lalrpop = "0.13.1"
5 changes: 5 additions & 0 deletions day12/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extern crate lalrpop;

fn main() {
lalrpop::process_root().unwrap();
}
6 changes: 6 additions & 0 deletions day12/src/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# parser.rs is generated at compile time by parser.lalrpop,
# so is not necessary.
# Ideally, we could express the rule "<n:*>.rs iff <n>.lalrpop",
# but if that's possible in a standard .gitignore file,
# I don't know how to do it.
parser.rs
81 changes: 81 additions & 0 deletions day12/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
pub mod parser;
pub use parser::parse_connections;

type Connections = (usize, Vec<usize>);
type Graph = Vec<bool>;

fn map_connections(graph: &mut Graph, connections: &[Connections], node: usize) {
map_connections_generic(graph, connections, node, true, &|b: bool| !b);
}

fn map_connections_node(
graph: &mut Vec<Node>,
connections: &[Connections],
node: usize,
group: usize,
) {
map_connections_generic(graph, connections, node, Some(group), &|n| n.is_none());
}

fn map_connections_generic<N, IsFalse>(
graph: &mut Vec<N>,
connections: &[Connections],
node: usize,
true_value: N,
is_false: &IsFalse,
) where
N: Copy,
IsFalse: Fn(N) -> bool,
{
graph[node] = true_value;
for &connection in connections[node].1.iter() {
if is_false(graph[connection]) {
map_connections_generic(graph, connections, connection, true_value, is_false);
}
}
}

pub fn connected_to_zero(connections: &[Connections]) -> Vec<usize> {
assert!(
connections.iter().enumerate().all(
|(idx, cxn)| idx == cxn.0,
),
"Input connections must be sorted by node index"
);
let mut graph = vec![false; connections.len()];
if graph.len() > 0 {
map_connections(&mut graph, connections, 0)
}
graph
.iter()
.enumerate()
.filter(|&(_, node)| *node)
.map(|(index, _)| index)
.collect()
}

type Node = Option<usize>;

pub fn count_groups(connections: &[Connections]) -> usize {
assert!(
connections.iter().enumerate().all(
|(idx, cxn)| idx == cxn.0,
),
"Input connections must be sorted by node index"
);
let mut graph: Vec<Node> = vec![None; connections.len()];
let mut group = 0;

while let Some(idx) = graph
.iter()
.enumerate()
.filter(|&(_, g)| g.is_none())
.map(|(idx, _)| idx)
.next()
{
group += 1;
map_connections_node(&mut graph, connections, idx, group);
}

group
}
17 changes: 17 additions & 0 deletions day12/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
extern crate day12;
use day12::{parse_connections, connected_to_zero, count_groups};

extern crate util;
use util::read_file;

fn main() {
let input = read_file("input.txt");
let connections = input
.trim()
.lines()
.map(|line| parse_connections(line).expect("Parse error"))
.collect::<Vec<_>>();
let zero_graph = connected_to_zero(&connections);
println!("# nodes connected to 0: {}", zero_graph.len());
println!("# groups: {}", count_groups(&connections));
}
24 changes: 24 additions & 0 deletions day12/src/parser.lalrpop
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use std::str::FromStr;

grammar;

pub connections: (usize, Vec<usize>) = {
<id:NodeId> "<->" <l:NodeIdList> => (id, l),
};

NodeId: usize = {
r"\d+" => usize::from_str(<>).unwrap(),
};

NodeIdList = CommaSeparatedList<NodeId>;

CommaSeparatedList<T>: Vec<T> = { // (1)
<v:(<T> ",")*> <e:T?> => match e { // (2)
None => v,
Some(e) => {
let mut v = v;
v.push(e);
v
}
}
};

0 comments on commit e66e734

Please sign in to comment.