advent-of-code

Perserverance, or the lack thereof

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

day-11.rs (7731B)

    1 use std::collections::HashMap;
    2 type Op = i128;
    3 
    4 fn main() {
    5     let input = std::fs::read_to_string("input.txt")
    6         .unwrap()
    7         .trim()
    8         .split(",")
    9         .map(|op| op.parse::<Op>().unwrap())
   10         .collect::<Vec<Op>>();
   11     println!("Rust:");
   12     println!("Part 1: {}", part_1(&input));
   13     println!("Part 2: ");
   14     part_2(&input);
   15 }
   16 
   17 fn turn_left(dir: (Op, Op)) -> (Op, Op) {
   18     // Takes in dir in the form of (0, 1).
   19     match dir {
   20         (0, 1) => (-1, 0),
   21         (1, 0) => (0, 1),
   22         (0, -1) => (1, 0),
   23         (-1, 0) => (0, -1),
   24         _ => panic!(),
   25     }
   26 }
   27 
   28 fn turn_right(dir: (Op, Op)) -> (Op, Op) {
   29     // Takes in dir in the form of (0, 1).
   30     match dir {
   31         (0, 1) => (1, 0),
   32         (1, 0) => (0, -1),
   33         (0, -1) => (-1, 0),
   34         (-1, 0) => (0, 1),
   35         _ => panic!(),
   36     }
   37 }
   38 
   39 fn robot(program: &Vec<Op>, panels: &mut HashMap<(Op, Op), usize>) {
   40     let mut program = program
   41         .iter()
   42         .enumerate()
   43         .map(|(idx, &op)| (idx, op))
   44         .collect::<HashMap<usize, Op>>();
   45     let mut pc = 0;
   46     let mut relative_base = 0;
   47     let mut coord = (0, 0);
   48     // Initially facing north.
   49     let mut dir = (0, 1);
   50 
   51     loop {
   52         // Get color for current panel. Panels start black (0).
   53         // This records only panels the robot has stepped on.
   54         let color = panels.entry(coord).or_insert(0);
   55         let (finished, step_output) = computer_step(
   56             &mut program,
   57             &mut pc,
   58             &mut relative_base,
   59             Some(*color as Op),
   60         );
   61         // First instruction is color to paint the panel to.
   62         *panels.get_mut(&coord).unwrap() = step_output[0] as usize;
   63         // Second instruction is direction to turn the robot.
   64         if step_output[1] == 0 {
   65             dir = turn_left(dir);
   66         } else {
   67             dir = turn_right(dir);
   68         }
   69         // Move robot forward by one panel.
   70         coord = (coord.0 + dir.0, coord.1 + dir.1);
   71         if finished {
   72             break;
   73         }
   74     }
   75 }
   76 
   77 fn computer_step(
   78     program: &mut HashMap<usize, Op>,
   79     pc: &mut usize,
   80     relative_base: &mut Op,
   81     mut input: Option<Op>,
   82 ) -> (bool, Vec<Op>) {
   83     let mut output = vec![];
   84     let get_param = |loc: usize, mode, program: &HashMap<usize, Op>, relative_base: &Op| -> Op {
   85         match mode {
   86             // Position mode.
   87             0 => *program.get(&(program[&loc] as usize)).unwrap_or(&0),
   88             // Immediate mode.
   89             1 => program[&loc],
   90             // Relative mode.
   91             2 => *program
   92                 .get(&((*relative_base + program[&loc]) as usize))
   93                 .unwrap_or(&0),
   94             _ => panic!(),
   95         }
   96     };
   97 
   98     let get_res_loc =
   99         |loc: usize, mode, program: &HashMap<usize, Op>, relative_base: &Op| -> usize {
  100             match mode {
  101                 // Position mode.
  102                 0 => program[&loc] as usize,
  103                 // Immediate mode is banned.
  104                 // Relative mode.
  105                 2 => (relative_base + program[&loc]) as usize,
  106                 _ => panic!(),
  107             }
  108         };
  109 
  110     loop {
  111         let op_code = program[pc] % 100;
  112         let mode_1 = (program[pc] % 1000) / 100;
  113         let mode_2 = (program[pc] % 10000) / 1000;
  114         let mode_3 = (program[pc] % 100000) / 10000;
  115         match op_code {
  116             1 => {
  117                 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
  118                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  119                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
  120                 *program.entry(res_loc).or_default() = param_1 + param_2;
  121                 *pc += 4;
  122             }
  123             2 => {
  124                 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
  125                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  126                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
  127                 *program.entry(res_loc as usize).or_default() = param_1 * param_2;
  128                 *pc += 4;
  129             }
  130             3 => {
  131                 let res_loc = get_res_loc(*pc + 1, mode_1, &program, relative_base);
  132                 // Only use input once.
  133                 if input.is_some() {
  134                     *program.entry(res_loc as usize).or_default() = input.unwrap();
  135                     input = None;
  136                 } else {
  137                     // Need new input.
  138                     return (false, output);
  139                 };
  140                 *pc += 2;
  141             }
  142             4 => {
  143                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  144                 output.push(param_1);
  145                 *pc += 2;
  146             }
  147             5 => {
  148                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  149                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
  150                 if param_1 != 0 {
  151                     *pc = param_2 as usize;
  152                 } else {
  153                     *pc += 3;
  154                 }
  155             }
  156             6 => {
  157                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  158                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
  159                 if param_1 == 0 {
  160                     *pc = param_2 as usize;
  161                 } else {
  162                     *pc += 3;
  163                 }
  164             }
  165             7 => {
  166                 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
  167                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  168                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
  169                 *program.entry(res_loc as usize).or_default() =
  170                     if param_1 < param_2 { 1 } else { 0 };
  171                 *pc += 4;
  172             }
  173             8 => {
  174                 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
  175                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  176                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
  177                 *program.entry(res_loc as usize).or_default() =
  178                     if param_1 == param_2 { 1 } else { 0 };
  179                 *pc += 4;
  180             }
  181             9 => {
  182                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  183                 *relative_base += param_1;
  184                 *pc += 2;
  185             }
  186             99 => {
  187                 break;
  188             }
  189             _ => {
  190                 println!("Unknown op {}", op_code);
  191                 println!("{}", program[pc]);
  192                 panic!();
  193             }
  194         }
  195     }
  196 
  197     (true, output)
  198 }
  199 
  200 fn part_1(input: &Vec<Op>) -> usize {
  201     let mut panels = HashMap::new();
  202     robot(input, &mut panels);
  203     panels.len()
  204 }
  205 
  206 fn part_2(input: &Vec<Op>) {
  207     let mut panels = HashMap::new();
  208     // Set initial panel white.
  209     panels.insert((0, 0), 1);
  210     robot(input, &mut panels);
  211     // Display the panel results.
  212     // Check max/min coordinates.
  213     let mut min_x = Op::max_value();
  214     let mut max_x = Op::min_value();
  215     let mut min_y = Op::max_value();
  216     let mut max_y = Op::min_value();
  217     for panel in panels.keys() {
  218         min_x = min_x.min(panel.0);
  219         max_x = max_x.max(panel.0);
  220         min_y = min_y.min(panel.1);
  221         max_y = max_y.max(panel.1);
  222     }
  223     // Print the panels.
  224     for y in (min_y..=max_y).rev() {
  225         for x in min_x..=max_x {
  226             match *panels.get(&(x, y)).unwrap_or(&0) {
  227                 1 => print!("██"),
  228                 _ => print!("  "),
  229             }
  230         }
  231         println!();
  232     }
  233 }