advent-of-code

Perserverance, or the lack thereof

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

day-13.rs (8372B)

    1 use std::collections::HashMap;
    2 use std::io::prelude::*;
    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: ");
   15     part_2(&input);
   16 }
   17 
   18 fn computer_step(
   19     program: &mut HashMap<usize, Op>,
   20     pc: &mut usize,
   21     relative_base: &mut Op,
   22     input: &mut Option<Op>,
   23 ) -> (bool, Vec<Op>) {
   24     let mut output = vec![];
   25     let get_param = |loc: usize, mode, program: &HashMap<usize, Op>, relative_base: &Op| -> Op {
   26         match mode {
   27             // Position mode.
   28             0 => *program.get(&(program[&loc] as usize)).unwrap_or(&0),
   29             // Immediate mode.
   30             1 => program[&loc],
   31             // Relative mode.
   32             2 => *program
   33                 .get(&((*relative_base + program[&loc]) as usize))
   34                 .unwrap_or(&0),
   35             _ => panic!(),
   36         }
   37     };
   38 
   39     let get_res_loc =
   40         |loc: usize, mode, program: &HashMap<usize, Op>, relative_base: &Op| -> usize {
   41             match mode {
   42                 // Position mode.
   43                 0 => program[&loc] as usize,
   44                 // Immediate mode is banned.
   45                 // Relative mode.
   46                 2 => (relative_base + program[&loc]) as usize,
   47                 _ => panic!(),
   48             }
   49         };
   50 
   51     loop {
   52         let op_code = program[pc] % 100;
   53         let mode_1 = (program[pc] % 1000) / 100;
   54         let mode_2 = (program[pc] % 10000) / 1000;
   55         let mode_3 = (program[pc] % 100000) / 10000;
   56         match op_code {
   57             1 => {
   58                 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
   59                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
   60                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
   61                 *program.entry(res_loc).or_default() = param_1 + param_2;
   62                 *pc += 4;
   63             }
   64             2 => {
   65                 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
   66                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
   67                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
   68                 *program.entry(res_loc as usize).or_default() = param_1 * param_2;
   69                 *pc += 4;
   70             }
   71             3 => {
   72                 let res_loc = get_res_loc(*pc + 1, mode_1, &program, relative_base);
   73                 // Only use input once.
   74                 if input.is_some() {
   75                     *program.entry(res_loc as usize).or_default() = input.unwrap();
   76                     *input = None;
   77                 } else {
   78                     // Need new input.
   79                     return (false, output);
   80                 };
   81                 *pc += 2;
   82             }
   83             4 => {
   84                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
   85                 output.push(param_1);
   86                 *pc += 2;
   87             }
   88             5 => {
   89                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
   90                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
   91                 if param_1 != 0 {
   92                     *pc = param_2 as usize;
   93                 } else {
   94                     *pc += 3;
   95                 }
   96             }
   97             6 => {
   98                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
   99                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
  100                 if param_1 == 0 {
  101                     *pc = param_2 as usize;
  102                 } else {
  103                     *pc += 3;
  104                 }
  105             }
  106             7 => {
  107                 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
  108                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  109                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
  110                 *program.entry(res_loc as usize).or_default() =
  111                     if param_1 < param_2 { 1 } else { 0 };
  112                 *pc += 4;
  113             }
  114             8 => {
  115                 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
  116                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  117                 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
  118                 *program.entry(res_loc as usize).or_default() =
  119                     if param_1 == param_2 { 1 } else { 0 };
  120                 *pc += 4;
  121             }
  122             9 => {
  123                 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
  124                 *relative_base += param_1;
  125                 *pc += 2;
  126             }
  127             99 => {
  128                 break;
  129             }
  130             _ => {
  131                 println!("Unknown op {}", op_code);
  132                 println!("{}", program[pc]);
  133                 panic!();
  134             }
  135         }
  136     }
  137 
  138     (true, output)
  139 }
  140 
  141 fn arcade(program: &Vec<Op>, demo: bool, cheat: bool) -> HashMap<(Op, Op), usize> {
  142     let mut program = program
  143         .iter()
  144         .enumerate()
  145         .map(|(idx, &op)| (idx, op))
  146         .collect::<HashMap<usize, Op>>();
  147     if !demo {
  148         *program.entry(0).or_default() = 2;
  149         if cheat {
  150             // Replace bottom opening with walls.
  151             (1583..1622).for_each(|i| {
  152                 *program.get_mut(&i).unwrap() = 1;
  153             });
  154         }
  155     }
  156     // Set program state.
  157     let mut pc = 0;
  158     let mut relative_base = 0;
  159     let mut screen = HashMap::<(Op, Op), usize>::new();
  160     let mut command = None;
  161     // Prepare for user input.
  162     let stdin = std::io::stdin();
  163     let mut user_input = [0; 1];
  164     // Main loop.
  165     loop {
  166         let (finished, step_output) =
  167             computer_step(&mut program, &mut pc, &mut relative_base, &mut command);
  168         // Parse step_output.
  169         step_output
  170             .chunks_exact(3)
  171             .to_owned()
  172             .for_each(|cmd| *screen.entry((cmd[0], cmd[1])).or_default() = cmd[2] as usize);
  173         if !demo {
  174             if cheat {
  175                 command = Some(-1);
  176                 // Break when there's no block left.
  177                 if count_blocks(&screen) == 0 {
  178                     break;
  179                 }
  180             } else {
  181                 draw(&screen);
  182                 // Read next input.
  183                 println!("Move left with <h>, and move right with <l>:");
  184                 while command.is_none() {
  185                     let mut handle = stdin.lock();
  186                     handle.read(&mut user_input[..]).ok();
  187                     match user_input[0] as char {
  188                         'h' => command = Some(-1),
  189                         'l' => command = Some(1),
  190                         _ => command = Some(0),
  191                     }
  192                 }
  193             }
  194         }
  195         if finished {
  196             break;
  197         }
  198     }
  199     screen
  200 }
  201 
  202 fn count_blocks(screen: &HashMap<(Op, Op), usize>) -> usize {
  203     screen.values().filter(|x| **x == 2).count()
  204 }
  205 
  206 fn draw(screen: &HashMap<(Op, Op), usize>) {
  207     // Check max coordinates.
  208     let mut max_x = Op::min_value();
  209     let mut max_y = Op::min_value();
  210     // Find score.
  211     let mut score = 0;
  212     for panel in screen.keys() {
  213         max_x = max_x.max(panel.0);
  214         max_y = max_y.max(panel.1);
  215         if *panel == (-1, 0) {
  216             score = screen[panel];
  217         }
  218     }
  219     // Print the panels.
  220     for y in 0..=max_y {
  221         for x in 0..=max_x {
  222             match *screen.get(&(x, y)).unwrap_or(&0) {
  223                 // Wall
  224                 1 => print!("||"),
  225                 // Block
  226                 2 => print!("##"),
  227                 // Paddle
  228                 3 => print!("--"),
  229                 // Ball
  230                 4 => print!("[]"),
  231                 // Empty
  232                 _ => print!("  "),
  233             }
  234         }
  235         println!();
  236     }
  237     // Print score.
  238     println!("score: {}", score);
  239 }
  240 
  241 fn part_1(input: &Vec<Op>) -> usize {
  242     let screen = arcade(input, true, false);
  243     count_blocks(&screen)
  244 }
  245 
  246 fn part_2(input: &Vec<Op>) {
  247     // Enable cheat and draw last screen for scores.
  248     let screen = arcade(input, false, true);
  249     draw(&screen);
  250 }