Skip to content

Commit

Permalink
Year 2016: Day 11
Browse files Browse the repository at this point in the history
  • Loading branch information
joshleaves committed Mar 19, 2024
1 parent 0bdd00b commit 0999576
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ 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)

## [2016.11.1]
### Added
- Solved [exercice for 2016, day 11](src/year_2016/day_11.rs).

## [2016.10.1]
### Added
- Solved [exercice for 2016, day 10](src/year_2016/day_10.rs).
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "advent-rs"
version = "2016.10.1"
version = "2016.11.1"
edition = "2021"
authors = ["Arnaud 'red' Rouyer"]
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,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 (like me) discovering Rust:
- [2015, complete!](NOTES_2015.md)
- [2016, up to day 10](NOTES_2016.md)
- [2016, up to day 11](NOTES_2016.md)

# Regarding style rules
I'm gonna use a mix of what `cargo fmt` does, with some stuff that feels more natural to me.
Expand Down
4 changes: 4 additions & 0 deletions inputs/year_2016/day_11_input
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The first floor contains a polonium generator, a thulium generator, a thulium-compatible microchip, a promethium generator, a ruthenium generator, a ruthenium-compatible microchip, a cobalt generator, and a cobalt-compatible microchip.
The second floor contains a polonium-compatible microchip and a promethium-compatible microchip.
The third floor contains nothing relevant.
The fourth floor contains nothing relevant.
10 changes: 10 additions & 0 deletions src/year_2016.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod day_07;
pub mod day_08;
pub mod day_09;
pub mod day_10;
pub mod day_11;

pub fn solve(day: u8, part: u8, input: impl Into<String>) -> Option<String> {
if part != 1 && part != 2 {
Expand Down Expand Up @@ -112,4 +113,13 @@ mod tests {
assert_eq!(day_10::day_10_v1(input), 157);
assert_eq!(day_10::day_10_v2(input), 1085);
}

#[test]
fn day_11() {
let input = include_str!("../inputs/year_2016/day_11_input");
assert_eq!(day_11::day_11_v1(input), 47);
if env::var("CI").is_err() {
assert_eq!(day_11::day_11_v2(input), 71);
}
}
}
160 changes: 160 additions & 0 deletions src/year_2016/day_11.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
use itertools::Itertools;
use regex::Regex;
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::collections::HashSet;

fn parse_world(input: &str) -> Vec<u8> {
let mut result: Vec<u8> = vec![0];
let mut chips: HashMap<&str, u8> = HashMap::new();
let mut gens: HashMap<&str, u8> = HashMap::new();
let mut alltypes: BTreeSet<&str> = BTreeSet::new();
let re_chips: Regex = Regex::new(r"(\w+)-compatible").unwrap();
let re_gens: Regex = Regex::new(r"(\w+) generator").unwrap();

for (floor, line) in input.lines().enumerate() {
for (_, [capture]) in re_chips.captures_iter(line).map(|c| c.extract()) {
alltypes.insert(capture);
chips.insert(capture, floor as u8);
}
for (_, [capture]) in re_gens.captures_iter(line).map(|c| c.extract()) {
alltypes.insert(capture);
gens.insert(capture, floor as u8);
}
}
// alltypes.sort()
for element in alltypes {
result.push(*chips.get(element).unwrap());
result.push(*gens.get(element).unwrap());
}

result
}

fn state_is_safe(state: &Vec<u8>) -> bool {
let pairs: Vec<Vec<u8>> = state[1..].chunks(2).map(|s| s.into()).collect();
for (idx_out, pair_out) in pairs.iter().enumerate() {
if pair_out[0] == pair_out[1] {
continue;
}

for (idx_in, pair_in) in pairs.iter().enumerate() {
if idx_in == idx_out {
continue;
}
if pair_out[0] == pair_in[1] {
return false;
}
}
}
true
}

fn next_moves_from(current_state: &Vec<u8>, idx: usize, go_up: bool) -> Vec<Vec<u8>> {
let mut new_moves: Vec<Vec<u8>> = vec![];
let mut new_state: Vec<u8> = current_state.to_owned();
if go_up {
new_state[0] += 1;
new_state[idx] += 1;
} else {
new_state[0] -= 1;
new_state[idx] -= 1;
}
if state_is_safe(&new_state) {
new_moves.push(new_state.to_vec());
}
for (idx_in, item_in) in current_state[idx + 1..].iter().enumerate() {
if *item_in != current_state[0] {
continue;
}
let mut new_state_in: Vec<u8> = current_state.to_owned();
if go_up {
new_state_in[0] += 1;
new_state_in[idx] += 1;
new_state_in[idx_in + idx + 1] += 1;
} else {
new_state_in[0] -= 1;
new_state_in[idx] -= 1;
new_state_in[idx_in + idx + 1] -= 1;
}
if state_is_safe(&new_state_in) {
new_moves.push(new_state_in.to_vec());
}
}

new_moves
}

fn next_moves(current_state: Vec<u8>) -> Vec<Vec<u8>> {
let mut new_moves: Vec<Vec<u8>> = vec![];
for (idx, item) in current_state.iter().enumerate() {
if idx == 0 || *item != current_state[0] {
continue;
}
if current_state[0] < 3 {
let mut up_moves: Vec<Vec<u8>> = next_moves_from(&current_state, idx, true);
new_moves.append(&mut up_moves);
}
if current_state[0] > 0 {
let mut down_moves: Vec<Vec<u8>> = next_moves_from(&current_state, idx, false);
new_moves.append(&mut down_moves);
}
}

new_moves
}

fn search(states: Vec<Vec<u8>>, mut moves_tried: HashSet<Vec<u8>>, depth: u16) -> u16 {
// println!("NEXT MOVES ({depth}) => {}", states.len());
if states
.iter()
.any(|state| state.iter().all(|floor| *floor == 3))
{
return depth;
}
let mut next_states: Vec<Vec<u8>> = vec![];
for state in states.iter().sorted().rev() {
if moves_tried.contains(state) {
continue;
}
moves_tried.insert(state.to_owned());
for next_state in next_moves(state.to_vec()).iter() {
if moves_tried.contains(next_state) {
continue;
}
next_states.push(next_state.to_vec());
}
}

search(next_states, moves_tried, depth + 1)
}

pub fn day_11_v1(input: impl Into<String>) -> u16 {
let floors: Vec<u8> = parse_world(&input.into());
let moves_tried: HashSet<Vec<u8>> = HashSet::new();
search(vec![floors], moves_tried, 0)
}

pub fn day_11_v2(input: impl Into<String>) -> u16 {
let mut floors: Vec<u8> = parse_world(&input.into());
floors.append(&mut vec![0, 0, 0, 0]);
let moves_tried: HashSet<Vec<u8>> = HashSet::new();
search(vec![floors], moves_tried, 0)
}

solvable!(day_11, day_11_v1, day_11_v2, u16);

#[cfg(test)]
mod tests {
use super::*;

const SAMPLE_ONE: &str = "The first floor contains a hydrogen-compatible microchip and a lithium-compatible microchip.\n\
The second floor contains a hydrogen generator.\n\
The third floor contains a lithium generator.\n\
The fourth floor contains nothing relevant.";

#[test]
fn works_with_samples_v1() {
assert_eq!(day_11_v1(SAMPLE_ONE), 11);
}
}

0 comments on commit 0999576

Please sign in to comment.