summaryrefslogtreecommitdiff
path: root/d21/src/main.rs
diff options
context:
space:
mode:
authorPrefetch2023-02-25 11:41:27 +0100
committerPrefetch2023-02-25 11:41:27 +0100
commit3b877bf4cc667eb8bcc787d145203600a4dba2d2 (patch)
treec1d247def29fcb58ae28e4ae4e4d73d1b4e1b27f /d21/src/main.rs
Initial commit
Diffstat (limited to 'd21/src/main.rs')
-rw-r--r--d21/src/main.rs109
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");
+ }
+}