diff options
Diffstat (limited to 'd23/src')
| -rw-r--r-- | d23/src/main.rs | 149 | 
1 files changed, 149 insertions, 0 deletions
diff --git a/d23/src/main.rs b/d23/src/main.rs new file mode 100644 index 0000000..234ebb6 --- /dev/null +++ b/d23/src/main.rs @@ -0,0 +1,149 @@ +use std::collections::HashMap; +use std::fs; + +// Compared to day 12, for day 23 it makes more sense to abstract +// over operands in this way; they could be registers or literals. +#[derive(Copy, Clone, Debug)] +enum Arg<'a> { +    Reg(&'a str), +    Lit(isize), +} + +// It's convenient to use the `From' trait for parsing operands +impl<'a> From<&'a str> for Arg<'a> { +    fn from(s: &'a str) -> Self { +        if s.chars().all(|c| c.is_ascii_digit() || c == '-') { // yes I know +            Self::Lit(s.parse().unwrap()) +        } else { +            Self::Reg(s) +        } +    } +} + +#[derive(Copy, Clone, Debug)] +enum Oper<'a> { +    Inc(Arg<'a>), +    Dec(Arg<'a>), +    Tgl(Arg<'a>), +    Cpy(Arg<'a>, Arg<'a>), +    Jnz(Arg<'a>, Arg<'a>), +} + +fn parse(lines: Vec<&str>) -> Vec<Oper> { +    let mut insts = Vec::new(); + +    for line in lines { +        let words: Vec<&str> = line.split_ascii_whitespace().collect(); + +        if words[0] == "inc" { +            insts.push(Oper::Inc(Arg::from(words[1]))); +        } else if words[0] == "dec" { +            insts.push(Oper::Dec(Arg::from(words[1]))); +        } else if words[0] == "tgl" { +            insts.push(Oper::Tgl(Arg::from(words[1]))); +        } else if words[0] == "cpy" { +            insts.push(Oper::Cpy(Arg::from(words[1]), Arg::from(words[2]))); +        } else if words[0] == "jnz" { +            insts.push(Oper::Jnz(Arg::from(words[1]), Arg::from(words[2]))); +        } +    } + +    insts +} + +fn execute(program: &Vec<Oper>, init: Vec<(&str, isize)>) -> isize { +    // Make a local mutable copy of the program +    let mut insts = program.clone(); + +    // `init' contains initial register values +    let mut state: HashMap<&str, isize> = init.into_iter().collect(); + +    let mut ip = 0; // instruction pointer +    while ip < insts.len() { +        match insts[ip] { +            Oper::Inc(arg) => { +                if let Arg::Reg(dst) = arg { +                    *state.entry(dst).or_insert(0) += 1; +                } +            }, +            Oper::Dec(arg) => { +                if let Arg::Reg(dst) = arg { +                    *state.entry(dst).or_insert(0) -= 1; +                } +            }, +            Oper::Tgl(arg) => { +                let off = match arg { +                    Arg::Reg(reg) => state.get(®).copied().unwrap_or(0), +                    Arg::Lit(lit) => lit, +                }; +                // Calculate index of target instruction +                let t = ((ip as isize) + off) as usize; +                if t < insts.len() { +                    insts[t] = match insts[t] { +                        Oper::Inc(a)      => Oper::Dec(a), +                        Oper::Dec(a)      => Oper::Inc(a), +                        Oper::Tgl(a)      => Oper::Inc(a), +                        Oper::Cpy(a1, a2) => Oper::Jnz(a1, a2), +                        Oper::Jnz(a1, a2) => Oper::Cpy(a1, a2), +                    }; +                } +            }, +            Oper::Cpy(arg1, arg2) => { +                // If destination is a literal (due to `Tgl'), ignore this `Cpy' +                if let Arg::Reg(dst) = arg2 { +                    let val = match arg1 { +                        Arg::Reg(src) => state.get(&src).copied().unwrap_or(0), +                        Arg::Lit(lit) => lit, +                    }; +                    *state.entry(dst).or_insert(0) = val; +                } +            }, +            Oper::Jnz(arg1, arg2) => { +                let val = match arg1 { +                    Arg::Reg(reg) => state.get(®).copied().unwrap_or(0), +                    Arg::Lit(lit) => lit, +                }; +                // Should the branch be taken? If yes: +                if val != 0 { +                    let off = match arg2 { +                        Arg::Reg(reg) => state.get(®).copied().unwrap_or(0), +                        Arg::Lit(lit) => lit, +                    }; +                    // Subtract 1 to compensate for `ip += 1;' below +                    ip = ((ip as isize) + off - 1) as usize; +                } +            }, +        }; + +        ip += 1; +    } + +    state.get("a").copied().unwrap() +} + +fn main() { +    // Read assembly from input text file and parse it +    let input = fs::read_to_string("input.txt").unwrap(); +    let lines = input.lines().collect(); +    let insts = parse(lines); + +    // Part 1 gives 14065 for my input +    println!("Part 1 solution: {}", execute(&insts, vec![("a", 7)])); + +    // Part 2 gives 479010625 for my input +    println!("Part 2 solution: {}", execute(&insts, vec![("a", 12)])); +} + +#[cfg(test)] +mod tests { +    use super::*; + +    #[test] +    fn part1_example1() { +        let lines = vec![ +            "cpy 2 a", "tgl a", "tgl a", "tgl a", "cpy 1 a", "dec a", "dec a" +        ]; +        let insts = parse(lines); +        assert_eq!(execute(&insts, vec![]), 3); +    } +}  | 
