diff options
Diffstat (limited to 'd21/src')
-rw-r--r-- | d21/src/main.rs | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/d21/src/main.rs b/d21/src/main.rs new file mode 100644 index 0000000..34d5599 --- /dev/null +++ b/d21/src/main.rs @@ -0,0 +1,109 @@ +use std::fs; + +use itertools::Itertools; + +fn solve_part1(insts: &Vec<&str>, init: &str) -> String { + let mut chars: Vec<char> = init.chars().collect(); + + for inst in insts { + let mut next = chars.clone(); + + let words: Vec<&str> = inst.split_ascii_whitespace().collect(); + if words[0] == "swap" { + if words[1] == "position" { + // Swap the characters at indices `i' and `j' + let i: usize = words[2].parse().unwrap(); + let j: usize = words[5].parse().unwrap(); + next[i] = chars[j]; + next[j] = chars[i]; + } else if words[1] == "letter" { + // Swap all occurrences of characters `x' and `y' + let x = words[2].chars().nth(0).unwrap(); + let y = words[5].chars().nth(0).unwrap(); + for i in 0..next.len() { + if next[i] == x { + next[i] = y; + } else if next[i] == y { + next[i] = x; + } + } + } + } else if words[0] == "rotate" { + if words[1] == "left" { + let n: usize = words[2].parse().unwrap(); + next.rotate_left(n % chars.len()); + } else if words[1] == "right" { + let n: usize = words[2].parse().unwrap(); + next.rotate_right(n % chars.len()); + } else if words[1] == "based" { + // Get the index `i' of character `x' + let x = words[6].chars().nth(0).unwrap(); + let i = next.iter().position(|&c| c == x).unwrap(); + // Rotate right by an amount determined by `i' + let n = if i < 4 { i + 1 } else { i + 2 }; + next.rotate_right(n % chars.len()); + } + } else if words[0] == "reverse" { + let i: usize = words[2].parse().unwrap(); + let j: usize = words[4].parse().unwrap(); + next[i..j + 1].reverse(); + } else if words[0] == "move" { + let i: usize = words[2].parse().unwrap(); + let j: usize = words[5].parse().unwrap(); + let c = next.remove(i); + next.insert(j, c); + } + + chars = next; + } + + chars.into_iter().collect() +} + +fn solve_part2(insts: &Vec<&str>, target: &str) -> String { + let init = vec!['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; + + // The "good" approach would be to reverse the operations in `insts'. + // I'm too lazy for that: I just go through all permutations of the + // input characters, run `insts' as in part 1, and see if it's the one. + for perm in init.into_iter().permutations(8) { + let string: String = perm.iter().collect(); + if solve_part1(insts, &string) == target { + return string; + } + } + + String::new() // shouldn't happen +} + +fn main() { + // Read instructions from input text file + let input = fs::read_to_string("input.txt").unwrap(); + let insts = input.lines().collect(); + + // Part 1 gives "gfdhebac" for me + println!("Part 1 solution: {}", solve_part1(&insts, "abcdefgh")); + + // Part 2 gives "dhaegfbc" for me + println!("Part 2 solution: {}", solve_part2(&insts, "fbgdceah")); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn part1_example1() { + let insts = vec![ + "swap position 4 with position 0", + "swap letter d with letter b", + "reverse positions 0 through 4", + "rotate left 1 step", + "move position 1 to position 4", + "move position 3 to position 0", + "rotate based on position of letter b", + "rotate based on position of letter d", + ]; + assert_eq!(solve_part1(&insts, "abcde"), "decab"); + } +} |