From cf6737aa1878c92effa316d5b775fe5402612d09 Mon Sep 17 00:00:00 2001 From: red Date: Tue, 27 Feb 2024 00:15:56 +0100 Subject: [PATCH] Year 2015: Day 07 --- 2015.md | 15 ++ CHANGELOG.md | 5 + Cargo.toml | 2 +- README.md | 2 +- benches/advent-bench.rs | 13 ++ inputs/year_2015_day_07_input | 339 ++++++++++++++++++++++++++++++ inputs/year_2015_day_07_sample_v1 | 8 + src/year_2015.rs | 1 + src/year_2015/day_07.rs | 115 ++++++++++ tests/year_2015_test.rs | 10 +- 10 files changed, 507 insertions(+), 3 deletions(-) create mode 100644 inputs/year_2015_day_07_input create mode 100644 inputs/year_2015_day_07_sample_v1 create mode 100644 src/year_2015/day_07.rs diff --git a/2015.md b/2015.md index f8ac5cc..8c96d6d 100644 --- a/2015.md +++ b/2015.md @@ -114,3 +114,18 @@ year_2015::day_06/year_2015::day_06_v2 ``` I started doing a copy of my original "naive" algorithm, and as it was too slow, I decided to learn [how to pass closures in Rust](https://doc.rust-lang.org/book/ch13-01-closures.html). + +## Day 07: Some Assembly Required + +``` +test year_2015::day_07::tests::works_with_samples_v1 ... ok +test year_2015::day_07::tests::works_with_samples_v2 ... ok +test year_2015_day_07 ... ok + +year_2015::day_07/year_2015::day_07_v1 + time: [63.406 µs 63.509 µs 63.606 µs] +year_2015::day_07/year_2015::day_07_v2 + time: [129.92 µs 130.54 µs 131.09 µs] +``` + +This one was already complicated in Ruby, but it gets even worse when you have to deal with [Rust's lifetimes](https://doc.rust-lang.org/rust-by-example/scope/lifetime.html). The concept in itself is kinda okay to understand, but the way it has to be used sometimes makes no sense. I guess I'll get used to it with time. A [nice crate](https://docs.rs/advent-of-code/2022.0.66/src/advent_of_code/year2015/day07.rs.html) helped me see through it a bit more clearly. diff --git a/CHANGELOG.md b/CHANGELOG.md index d6a45bb..9cfb28b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ Of note: - The changelog 2015.5.2 has been rewritten from each commit content. - This file may be amended entirely in the future to adhere to the [GNU Changelog style](https://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html#Style-of-Change-Logs) +## [2015.7.1] +### Added +- Solved [exercice for 2015, day 7](src/year_2015/day_07.rs). + + ## [2015.6.1] ### Added - Solved [exercice for 2015, day 6](src/year_2015/day_06.rs). diff --git a/Cargo.toml b/Cargo.toml index 5757d0b..4004dab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "advent-rs" -version = "2015.6.1" +version = "2015.7.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index 9213567..e7ed3a0 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ As I said, [Consistency is hard](https://github.com/joshleaves/advent-rb), and I I'm also adding notes that may be useful if you're discovering Rust (like I am). Notes for solving: -* [2015, up to day 05](year_2015.md) +* [2015, up to day 07](year_2015.md) # Regarding style rules I'm gonna use a mix of what `cargo fmt` does, with some stuff that feels more natural to me. diff --git a/benches/advent-bench.rs b/benches/advent-bench.rs index 2f9a1a5..98f3fb7 100644 --- a/benches/advent-bench.rs +++ b/benches/advent-bench.rs @@ -4,6 +4,7 @@ use advent_rs::year_2015::day_03; use advent_rs::year_2015::day_04; use advent_rs::year_2015::day_05; use advent_rs::year_2015::day_06; +use advent_rs::year_2015::day_07; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use std::time::Duration; @@ -84,6 +85,18 @@ pub fn year_2015_benchmark(c: &mut Criterion) { b.iter(|| day_06::day_06_v2(black_box(input_year_2015_day_06))) }); g2015_06.finish(); + + let mut g2015_07 = c.benchmark_group("year_2015::day_07"); + g2015_07.warm_up_time(warm_up_time); + g2015_07.measurement_time(measurement_time); + let input_year_2015_day_07 = include_str!("../inputs/year_2015_day_07_input"); + g2015_07.bench_function("year_2015::day_07_v1", |b| { + b.iter(|| day_07::day_07_v1(black_box(input_year_2015_day_07))) + }); + g2015_07.bench_function("year_2015::day_07_v2", |b| { + b.iter(|| day_07::day_07_v2(black_box(input_year_2015_day_07))) + }); + g2015_07.finish(); } criterion_group!(benches, year_2015_benchmark); diff --git a/inputs/year_2015_day_07_input b/inputs/year_2015_day_07_input new file mode 100644 index 0000000..5080c3d --- /dev/null +++ b/inputs/year_2015_day_07_input @@ -0,0 +1,339 @@ +bn RSHIFT 2 -> bo +lf RSHIFT 1 -> ly +fo RSHIFT 3 -> fq +cj OR cp -> cq +fo OR fz -> ga +t OR s -> u +lx -> a +NOT ax -> ay +he RSHIFT 2 -> hf +lf OR lq -> lr +lr AND lt -> lu +dy OR ej -> ek +1 AND cx -> cy +hb LSHIFT 1 -> hv +1 AND bh -> bi +ih AND ij -> ik +c LSHIFT 1 -> t +ea AND eb -> ed +km OR kn -> ko +NOT bw -> bx +ci OR ct -> cu +NOT p -> q +lw OR lv -> lx +NOT lo -> lp +fp OR fv -> fw +o AND q -> r +dh AND dj -> dk +ap LSHIFT 1 -> bj +bk LSHIFT 1 -> ce +NOT ii -> ij +gh OR gi -> gj +kk RSHIFT 1 -> ld +lc LSHIFT 1 -> lw +lb OR la -> lc +1 AND am -> an +gn AND gp -> gq +lf RSHIFT 3 -> lh +e OR f -> g +lg AND lm -> lo +ci RSHIFT 1 -> db +cf LSHIFT 1 -> cz +bn RSHIFT 1 -> cg +et AND fe -> fg +is OR it -> iu +kw AND ky -> kz +ck AND cl -> cn +bj OR bi -> bk +gj RSHIFT 1 -> hc +iu AND jf -> jh +NOT bs -> bt +kk OR kv -> kw +ks AND ku -> kv +hz OR ik -> il +b RSHIFT 1 -> v +iu RSHIFT 1 -> jn +fo RSHIFT 5 -> fr +be AND bg -> bh +ga AND gc -> gd +hf OR hl -> hm +ld OR le -> lf +as RSHIFT 5 -> av +fm OR fn -> fo +hm AND ho -> hp +lg OR lm -> ln +NOT kx -> ky +kk RSHIFT 3 -> km +ek AND em -> en +NOT ft -> fu +NOT jh -> ji +jn OR jo -> jp +gj AND gu -> gw +d AND j -> l +et RSHIFT 1 -> fm +jq OR jw -> jx +ep OR eo -> eq +lv LSHIFT 15 -> lz +NOT ey -> ez +jp RSHIFT 2 -> jq +eg AND ei -> ej +NOT dm -> dn +jp AND ka -> kc +as AND bd -> bf +fk OR fj -> fl +dw OR dx -> dy +lj AND ll -> lm +ec AND ee -> ef +fq AND fr -> ft +NOT kp -> kq +ki OR kj -> kk +cz OR cy -> da +as RSHIFT 3 -> au +an LSHIFT 15 -> ar +fj LSHIFT 15 -> fn +1 AND fi -> fj +he RSHIFT 1 -> hx +lf RSHIFT 2 -> lg +kf LSHIFT 15 -> kj +dz AND ef -> eh +ib OR ic -> id +lf RSHIFT 5 -> li +bp OR bq -> br +NOT gs -> gt +fo RSHIFT 1 -> gh +bz AND cb -> cc +ea OR eb -> ec +lf AND lq -> ls +NOT l -> m +hz RSHIFT 3 -> ib +NOT di -> dj +NOT lk -> ll +jp RSHIFT 3 -> jr +jp RSHIFT 5 -> js +NOT bf -> bg +s LSHIFT 15 -> w +eq LSHIFT 1 -> fk +jl OR jk -> jm +hz AND ik -> im +dz OR ef -> eg +1 AND gy -> gz +la LSHIFT 15 -> le +br AND bt -> bu +NOT cn -> co +v OR w -> x +d OR j -> k +1 AND gd -> ge +ia OR ig -> ih +NOT go -> gp +NOT ed -> ee +jq AND jw -> jy +et OR fe -> ff +aw AND ay -> az +ff AND fh -> fi +ir LSHIFT 1 -> jl +gg LSHIFT 1 -> ha +x RSHIFT 2 -> y +db OR dc -> dd +bl OR bm -> bn +ib AND ic -> ie +x RSHIFT 3 -> z +lh AND li -> lk +ce OR cd -> cf +NOT bb -> bc +hi AND hk -> hl +NOT gb -> gc +1 AND r -> s +fw AND fy -> fz +fb AND fd -> fe +1 AND en -> eo +z OR aa -> ab +bi LSHIFT 15 -> bm +hg OR hh -> hi +kh LSHIFT 1 -> lb +cg OR ch -> ci +1 AND kz -> la +gf OR ge -> gg +gj RSHIFT 2 -> gk +dd RSHIFT 2 -> de +NOT ls -> lt +lh OR li -> lj +jr OR js -> jt +au AND av -> ax +0 -> c +he AND hp -> hr +id AND if -> ig +et RSHIFT 5 -> ew +bp AND bq -> bs +e AND f -> h +ly OR lz -> ma +1 AND lu -> lv +NOT jd -> je +ha OR gz -> hb +dy RSHIFT 1 -> er +iu RSHIFT 2 -> iv +NOT hr -> hs +as RSHIFT 1 -> bl +kk RSHIFT 2 -> kl +b AND n -> p +ln AND lp -> lq +cj AND cp -> cr +dl AND dn -> do +ci RSHIFT 2 -> cj +as OR bd -> be +ge LSHIFT 15 -> gi +hz RSHIFT 5 -> ic +dv LSHIFT 1 -> ep +kl OR kr -> ks +gj OR gu -> gv +he RSHIFT 5 -> hh +NOT fg -> fh +hg AND hh -> hj +b OR n -> o +jk LSHIFT 15 -> jo +gz LSHIFT 15 -> hd +cy LSHIFT 15 -> dc +kk RSHIFT 5 -> kn +ci RSHIFT 3 -> ck +at OR az -> ba +iu RSHIFT 3 -> iw +ko AND kq -> kr +NOT eh -> ei +aq OR ar -> as +iy AND ja -> jb +dd RSHIFT 3 -> df +bn RSHIFT 3 -> bp +1 AND cc -> cd +at AND az -> bb +x OR ai -> aj +kk AND kv -> kx +ao OR an -> ap +dy RSHIFT 3 -> ea +x RSHIFT 1 -> aq +eu AND fa -> fc +kl AND kr -> kt +ia AND ig -> ii +df AND dg -> di +NOT fx -> fy +k AND m -> n +bn RSHIFT 5 -> bq +km AND kn -> kp +dt LSHIFT 15 -> dx +hz RSHIFT 2 -> ia +aj AND al -> am +cd LSHIFT 15 -> ch +hc OR hd -> he +he RSHIFT 3 -> hg +bn OR by -> bz +NOT kt -> ku +z AND aa -> ac +NOT ak -> al +cu AND cw -> cx +NOT ie -> if +dy RSHIFT 2 -> dz +ip LSHIFT 15 -> it +de OR dk -> dl +au OR av -> aw +jg AND ji -> jj +ci AND ct -> cv +dy RSHIFT 5 -> eb +hx OR hy -> hz +eu OR fa -> fb +gj RSHIFT 3 -> gl +fo AND fz -> gb +1 AND jj -> jk +jp OR ka -> kb +de AND dk -> dm +ex AND ez -> fa +df OR dg -> dh +iv OR jb -> jc +x RSHIFT 5 -> aa +NOT hj -> hk +NOT im -> in +fl LSHIFT 1 -> gf +hu LSHIFT 15 -> hy +iq OR ip -> ir +iu RSHIFT 5 -> ix +NOT fc -> fd +NOT el -> em +ck OR cl -> cm +et RSHIFT 3 -> ev +hw LSHIFT 1 -> iq +ci RSHIFT 5 -> cl +iv AND jb -> jd +dd RSHIFT 5 -> dg +as RSHIFT 2 -> at +NOT jy -> jz +af AND ah -> ai +1 AND ds -> dt +jx AND jz -> ka +da LSHIFT 1 -> du +fs AND fu -> fv +jp RSHIFT 1 -> ki +iw AND ix -> iz +iw OR ix -> iy +eo LSHIFT 15 -> es +ev AND ew -> ey +ba AND bc -> bd +fp AND fv -> fx +jc AND je -> jf +et RSHIFT 2 -> eu +kg OR kf -> kh +iu OR jf -> jg +er OR es -> et +fo RSHIFT 2 -> fp +NOT ca -> cb +bv AND bx -> by +u LSHIFT 1 -> ao +cm AND co -> cp +y OR ae -> af +bn AND by -> ca +1 AND ke -> kf +jt AND jv -> jw +fq OR fr -> fs +dy AND ej -> el +NOT kc -> kd +ev OR ew -> ex +dd OR do -> dp +NOT cv -> cw +gr AND gt -> gu +dd RSHIFT 1 -> dw +NOT gw -> gx +NOT iz -> ja +1 AND io -> ip +NOT ag -> ah +b RSHIFT 5 -> f +NOT cr -> cs +kb AND kd -> ke +jr AND js -> ju +cq AND cs -> ct +il AND in -> io +NOT ju -> jv +du OR dt -> dv +dd AND do -> dq +b RSHIFT 2 -> d +jm LSHIFT 1 -> kg +NOT dq -> dr +bo OR bu -> bv +gk OR gq -> gr +he OR hp -> hq +NOT h -> i +hf AND hl -> hn +gv AND gx -> gy +x AND ai -> ak +bo AND bu -> bw +hq AND hs -> ht +hz RSHIFT 1 -> is +gj RSHIFT 5 -> gm +g AND i -> j +gk AND gq -> gs +dp AND dr -> ds +b RSHIFT 3 -> e +gl AND gm -> go +gl OR gm -> gn +y AND ae -> ag +hv OR hu -> hw +1674 -> b +ab AND ad -> ae +NOT ac -> ad +1 AND ht -> hu +NOT hn -> ho \ No newline at end of file diff --git a/inputs/year_2015_day_07_sample_v1 b/inputs/year_2015_day_07_sample_v1 new file mode 100644 index 0000000..82b0bf0 --- /dev/null +++ b/inputs/year_2015_day_07_sample_v1 @@ -0,0 +1,8 @@ +123 -> x +456 -> b +x AND g -> a +x OR b -> e +x LSHIFT 2 -> f +b RSHIFT 2 -> g +NOT x -> h +NOT b -> i diff --git a/src/year_2015.rs b/src/year_2015.rs index 7e14feb..d1ff7dd 100644 --- a/src/year_2015.rs +++ b/src/year_2015.rs @@ -4,6 +4,7 @@ pub mod day_03; pub mod day_04; pub mod day_05; pub mod day_06; +pub mod day_07; pub fn solve(day: u8, version: u8, input: String) -> Option { match (day, version) { diff --git a/src/year_2015/day_07.rs b/src/year_2015/day_07.rs new file mode 100644 index 0000000..f7e5e5e --- /dev/null +++ b/src/year_2015/day_07.rs @@ -0,0 +1,115 @@ +use std::collections::HashMap; + +enum Operation<'a> { + Assign(&'a str), + Not(&'a str), + And(&'a str, &'a str), + Or(&'a str, &'a str), + LeftShift(&'a str, &'a str), + RightShift(&'a str, &'a str), +} + +struct LogicalGate<'a> { + operation: Operation<'a>, + value: Option, +} + +fn value_of<'a>(gates: &mut HashMap<&'a str, LogicalGate<'a>>, wire: &'a str) -> Option { + if let Ok(value) = wire.parse::() { + return Some(value); + } else { + return find_value_of_wire(gates, wire); + } +} + +fn find_value_of_wire<'a>( + gates: &mut HashMap<&'a str, LogicalGate<'a>>, + wire: &'a str, +) -> Option { + let Some(gate) = gates.get(wire) else { + panic!("Invalid wire: {}", wire); + }; + if gate.value.is_some() { + return gate.value; + } + let wire_value = match gate.operation { + Operation::Assign(value) => value_of(gates, value)?, + Operation::Not(value) => !value_of(gates, value)?, + Operation::And(lhs, rhs) => value_of(gates, lhs)? & value_of(gates, rhs)?, + Operation::Or(lhs, rhs) => value_of(gates, lhs)? | value_of(gates, rhs)?, + Operation::LeftShift(lhs, rhs) => value_of(gates, lhs)? << value_of(gates, rhs)?, + Operation::RightShift(lhs, rhs) => value_of(gates, lhs)? >> value_of(gates, rhs)?, + }; + gates.get_mut(wire)?.value = Some(wire_value); + Some(wire_value) +} + +fn create_gate<'a>(gates: &mut HashMap<&'a str, LogicalGate<'a>>, line: &'a str) { + let words = line.split(' ').collect::>(); + let (wire, operation) = match words.len() { + // "123 -> x" + 3 => (words[2], Operation::Assign(words[0])), + // "NOT e -> f". + 4 => (words[3], Operation::Not(words[1])), + 5 => match words[1] { + "AND" => (words[4], Operation::And(words[0], words[2])), + "OR" => (words[4], Operation::Or(words[0], words[2])), + "LSHIFT" => (words[4], Operation::LeftShift(words[0], words[2])), + "RSHIFT" => (words[4], Operation::RightShift(words[0], words[2])), + _ => panic!("Invalid line: {}", line), + }, + _ => panic!("Invalid line: {}", line), + }; + let gate = LogicalGate { + operation, + value: None, + }; + gates.insert(wire, gate); +} + +pub fn day_07_v1(input: &str) -> u32 { + let mut gates: HashMap<&str, LogicalGate> = HashMap::new(); + for line in input.lines() { + create_gate(&mut gates, line); + } + let Some(result) = value_of(&mut gates, "a") else { + panic!("Invalid result"); + }; + return result; +} + +pub fn day_07_v2(input: &str) -> u32 { + let mut gates: HashMap<&str, LogicalGate> = HashMap::new(); + for line in input.lines() { + create_gate(&mut gates, line); + } + let value_of_a = day_07_v1(input); + let value_a = format!("{}", value_of_a); + let gate = LogicalGate { + operation: Operation::Assign(value_a.as_str()), + value: Some(value_of_a), + }; + gates.remove("b"); + gates.insert("b", gate); + let Some(result) = value_of(&mut gates, "a") else { + panic!("Invalid result"); + }; + return result; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn works_with_samples_v1() { + let sample_one = include_str!("../../inputs/year_2015_day_07_sample_v1"); + assert_eq!(day_07_v1(sample_one), 114); + } + + #[test] + fn works_with_samples_v2() { + let sample_one = include_str!("../../inputs/year_2015_day_07_sample_v1"); + assert_eq!(day_07_v2(sample_one), 24); + } +} diff --git a/tests/year_2015_test.rs b/tests/year_2015_test.rs index 118dd5c..a65727c 100644 --- a/tests/year_2015_test.rs +++ b/tests/year_2015_test.rs @@ -4,6 +4,7 @@ use advent_rs::year_2015::day_03; use advent_rs::year_2015::day_04; use advent_rs::year_2015::day_05; use advent_rs::year_2015::day_06; +use advent_rs::year_2015::day_07; #[test] fn year_2015_day_01() { @@ -44,5 +45,12 @@ fn year_2015_day_05() { fn year_2015_day_06() { let input = include_str!("../inputs/year_2015_day_06_input"); assert_eq!(day_06::day_06_v1(input), 400_410); - // assert_eq!(day_06::day_06_v2(input), 15_343_601); + assert_eq!(day_06::day_06_v2(input), 15_343_601); +} + +#[test] +fn year_2015_day_07() { + let input = include_str!("../inputs/year_2015_day_07_input"); + assert_eq!(day_07::day_07_v1(input), 46_065); + assert_eq!(day_07::day_07_v2(input), 14_134); }