advent-of-code

Perserverance, or the lack thereof

git clone git://git.shimmy1996.com/advent-of-code.git

day-15.rs (9857B)

    1 use std::collections::HashMap;
    2 use std::collections::VecDeque;
    3 type Op = i128;
    4 
    5 fn main() {
    6     let input = std::fs::read_to_string("input.txt")
    7         .unwrap()
    8         .trim()
    9         .split(",")
   10         .map(|op| op.parse::<Op>().unwrap())
   11         .collect::<Vec<Op>>();
   12     println!("Rust:");
   13     println!("Part 1: {}", part_1(&input));
   14     println!("Part 2: {}", part_2(&input));
   15 }
   16 
   17 fn computer_step(
   18     program: &mut HashMap<usize, Op>,
   19     pc: &mut usize,
   20     relative_base: &mut Op,
   21     input: &mut Option<Op>,
   22 ) -> (bool, Vec<Op>) {
   23     let mut output = vec![];
   24     let get_param = |loc: usize, mode, program: &HashMap<usize, Op>, relative_base: &Op| -> Op {
   25         match mode {
   26             // Position mode.
   27             0 => *program.get(&(program[&loc] as usize)).unwrap_or(&0),
   28             // Immediate mode.
   29             1 => program[&loc],
   30             // Relative mode.
   31             2 => *program
   32                 .get(&((*relative_base + program[&loc]) as usize))
   33                 .unwrap_or(&0),
   34             _ => panic!(),
   35         }
   36     };
   37 
   38     let get_res_loc =
   39         |loc: usize, mode, program: &HashMap<usize, Op>, relative_base: &Op| -> usize {
   40             match mode {
   41                 // Position mode.
   42                 0 => program[&loc] as usize,
   43                 // Immediate mode is banned.
   44                 // Relative mode.
   45                 2 => (relative_base + program[&loc]) as usize,
   46                 _ => panic!(),
   47             }
   48         };
   49 
   50     loop {
   51         let op_code = program[pc] % 100;
   52         let mode_1 = (program[pc] % 1000) / 100;
   53         let mode_2 = (program[pc] % 10000) / 1000;
   54         let mode_3 = (program[pc] % 100000) / 10000;
   55         match op_code {
   56             1 => {
   57                 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
   58                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
   59                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
   60                 *program.entry(res_loc).or_default() = param_1 + param_2;
   61                 *pc += 4;
   62             }
   63             2 => {
   64                 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
   65                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
   66                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
   67                 *program.entry(res_loc as usize).or_default() = param_1 * param_2;
   68                 *pc += 4;
   69             }
   70             3 => {
   71                 let res_loc = get_res_loc(*pc + 1, mode_1, &program, relative_base);
   72                 // Only use input once.
   73                 if input.is_some() {
   74                     *program.entry(res_loc as usize).or_default() = input.unwrap();
   75                     *input = None;
   76                 } else {
   77                     // Need new input.
   78                     return (false, output);
   79                 };
   80                 *pc += 2;
   81             }
   82             4 => {
   83                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
   84                 output.push(param_1);
   85                 *pc += 2;
   86             }
   87             5 => {
   88                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
   89                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
   90                 if param_1 != 0 {
   91                     *pc = param_2 as usize;
   92                 } else {
   93                     *pc += 3;
   94                 }
   95             }
   96             6 => {
   97                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
   98                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
   99                 if param_1 == 0 {
  100                     *pc = param_2 as usize;
  101                 } else {
  102                     *pc += 3;
  103                 }
  104             }
  105             7 => {
  106                 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
  107                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  108                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
  109                 *program.entry(res_loc as usize).or_default() =
  110                     if param_1 < param_2 { 1 } else { 0 };
  111                 *pc += 4;
  112             }
  113             8 => {
  114                 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
  115                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  116                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
  117                 *program.entry(res_loc as usize).or_default() =
  118                     if param_1 == param_2 { 1 } else { 0 };
  119                 *pc += 4;
  120             }
  121             9 => {
  122                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  123                 *relative_base += param_1;
  124                 *pc += 2;
  125             }
  126             99 => {
  127                 break;
  128             }
  129             _ => {
  130                 println!("Unknown op {}", op_code);
  131                 println!("{}", program[pc]);
  132                 panic!();
  133             }
  134         }
  135     }
  136 
  137     (true, output)
  138 }
  139 
  140 fn flip(cmd: &i32) -> i32 {
  141     // Returns command in opposite direction.
  142     match cmd {
  143         1 => 2,
  144         2 => 1,
  145         3 => 4,
  146         4 => 3,
  147         _ => panic!(),
  148     }
  149 }
  150 
  151 fn move_to_cmd(coord: (i32, i32), cmd: i32) -> (i32, i32) {
  152     // Moves coord by cmd.
  153     match cmd {
  154         // North
  155         1 => (coord.0, coord.1 + 1),
  156         // South
  157         2 => (coord.0, coord.1 - 1),
  158         // West
  159         3 => (coord.0 - 1, coord.1),
  160         // East
  161         4 => (coord.0 + 1, coord.1),
  162         _ => panic!(),
  163     }
  164 }
  165 
  166 fn chart_maze(input: &Vec<Op>) -> HashMap<(i32, i32), i32> {
  167     // Chart the maze via DFS.
  168     let mut program = input
  169         .iter()
  170         .enumerate()
  171         .map(|(idx, &op)| (idx, op))
  172         .collect::<HashMap<usize, Op>>();
  173     // Set program state.
  174     let mut pc = 0;
  175     let mut relative_base = 0;
  176     // DFS to chart the maze.
  177     let mut curr_cmds = Vec::new();
  178     let mut curr_path = vec![(0, 0)];
  179     let mut map = HashMap::<(i32, i32), i32>::new();
  180     map.insert((0, 0), 1);
  181     // Start with initial directions.
  182     let mut queue = VecDeque::new();
  183     for cmd in 1..=4 {
  184         queue.push_back((1, cmd, move_to_cmd((0, 0), cmd)));
  185     }
  186     while let Some((path_len, cmd, coord)) = queue.pop_back() {
  187         // Check difference between current path and backtrack.
  188         if curr_path.len() > path_len {
  189             // Backtrack.
  190             for cmd in curr_cmds[(path_len - 1)..].iter().rev().map(flip) {
  191                 computer_step(
  192                     &mut program,
  193                     &mut pc,
  194                     &mut relative_base,
  195                     &mut Some(cmd.into()),
  196                 );
  197             }
  198             // Clean up path.
  199             curr_cmds.truncate(path_len - 1);
  200             // Keep the origin.
  201             curr_path.truncate(path_len);
  202         }
  203         // Move in specified direction.
  204         let (_, step_output) = computer_step(
  205             &mut program,
  206             &mut pc,
  207             &mut relative_base,
  208             &mut Some(cmd.into()),
  209         );
  210         // Chart the result.
  211         map.insert(coord, step_output[0] as i32);
  212         // Determine if we proceed in given direction.
  213         if step_output[0] != 0 {
  214             // Free space, record and plan next moves.
  215             // Otherwise, keep original path.
  216             curr_cmds.push(cmd);
  217             curr_path.push(coord);
  218             for next_cmd in 1..=4 {
  219                 // Only visit uncharted location.
  220                 let target = move_to_cmd(coord, next_cmd);
  221                 if !map.contains_key(&target) {
  222                     queue.push_back((path_len + 1, next_cmd, target));
  223                 }
  224             }
  225         }
  226     }
  227     map
  228 }
  229 
  230 fn find_min_path(map: &HashMap<(i32, i32), i32>) -> usize {
  231     // Return shortest path from (0, 0) to space labeled 2.
  232     let mut queue = VecDeque::new();
  233     let mut visited = HashMap::new();
  234     queue.push_back((0, (0, 0)));
  235     // BFS to find location.
  236     while let Some((path_len, coord)) = queue.pop_front() {
  237         visited.insert(coord, true);
  238         match map.get(&coord).unwrap_or(&0) {
  239             0 => {
  240                 continue;
  241             }
  242             1 => {
  243                 // Explore next steps.
  244                 for cmd in 1..=4 {
  245                     let next_coord = move_to_cmd(coord, cmd);
  246                     if !visited.get(&next_coord).unwrap_or(&false) {
  247                         queue.push_back((path_len + 1, next_coord));
  248                     }
  249                 }
  250             }
  251             2 => return path_len,
  252             _ => panic!(),
  253         }
  254     }
  255     usize::max_value()
  256 }
  257 
  258 fn calc_fill_time(map: &HashMap<(i32, i32), i32>) -> usize {
  259     // Computes the fill time for the entire maze.
  260     // Find oxygen source.
  261     let source_coord = *map.iter().filter(|(_, &v)| v == 2).next().unwrap().0;
  262     let mut queue = VecDeque::new();
  263     let mut filled = HashMap::new();
  264     queue.push_back((0, source_coord));
  265     let mut max_time = 0;
  266     // BFS to fill the maze.
  267     while let Some((time, coord)) = queue.pop_front() {
  268         max_time = max_time.max(time);
  269         filled.insert(coord, true);
  270         if *map.get(&coord).unwrap_or(&0) != 0 {
  271             // Walls can't be filled.
  272             // Fill the next area.
  273             for cmd in 1..=4 {
  274                 let next_coord = move_to_cmd(coord, cmd);
  275                 if !filled.get(&next_coord).unwrap_or(&false) {
  276                     queue.push_back((time + 1, next_coord));
  277                 }
  278             }
  279         }
  280     }
  281     max_time
  282 }
  283 
  284 fn part_1(input: &Vec<Op>) -> usize {
  285     let map = chart_maze(input);
  286     find_min_path(&map)
  287 }
  288 
  289 fn part_2(input: &Vec<Op>) -> usize {
  290     let map = chart_maze(input);
  291     calc_fill_time(&map)
  292 }