Solutions for 2022, as well as 2015-2018 and 2019 up to day 11

This commit is contained in:
Chris Alge 2023-03-12 15:20:02 +01:00
commit 1895197c49
722 changed files with 375457 additions and 0 deletions

View file

@ -0,0 +1,8 @@
[package]
name = "intcode_processor"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,485 @@
pub mod intcode_processor {
use std::{collections::VecDeque, num::ParseIntError};
type RegVal = isize;
/// The return conditions of the Cpu:
/// - Output(RegVal) if some return instruction ocurred mid-program (without a Halt immediately
/// succeeding it),
/// - DiagnosticCode(RegVal) if a return instruction with a Halt immediately after it was
/// encountered, or
/// - Halt if a Halt instruction was triggered that did not immediately follow an output
/// instruction
#[derive(PartialEq, Eq, Debug)]
pub enum OutputState { Output(RegVal), DiagnosticCode(RegVal), Halt }
/// The Cpu struct holds the state of our processor. It consists of `memory`, which serves
/// instructions as well as data, and `instr_ptr`, a pointer to the next instruction. It starts at 0.
/// Furthermore, there is an `input` register that is read by some instructions.
/// Element 0 is assumed to contain a valid opcode. The subsequent elements will be treated
/// as its operands. After executing an instruction, instr_ptr will be incremented by the number of
/// expected operands + 1 (for the instruction). Since instructions have different lengths, and
/// there is nothing preventing an instruction from overwriting a memory address that will be
/// considered an instruction later on, no guarantees can be given about the eventual memory
/// layout without examining the entire program.
///
/// `Cpu`s should be instantiated by `with_memory()` and their program executed by `run()`.
#[derive(Default, Clone)]
pub struct Cpu {
memory: Vec<RegVal>,
instr_ptr: usize,
input: VecDeque<RegVal>,
rel_base: RegVal,
}
impl Cpu {
/// Initialize a new Processor using the given input as the memory. The Instruction Pointer
/// starts at 0.
///
/// ## Example:
/// ````
/// use intcode_processor::intcode_processor::Cpu;
///
/// let mut cpu = Cpu::with_memory(vec![1, 2, 3, 4, 99]);
/// assert_eq!(cpu.get(0), 1);
/// assert_eq!(cpu.get(1), 2);
/// assert_eq!(cpu.get(2), 3);
/// assert_eq!(cpu.get(3), 4);
/// assert_eq!(cpu.get(4), 99);
/// ````
pub fn with_memory(memory: Vec<RegVal>) -> Self {
Self {
memory,
..Default::default()
}
}
pub fn try_with_memory_from_str(input: &str) -> Result<Self, ParseIntError> {
let mem: Result<Vec<_>, _> = input.split(',').map(|s| s.parse::<RegVal>()).collect();
match mem {
Ok(memory) => Ok(Self {
memory,
..Default::default()
}),
Err(e) => Err(e),
}
}
/// Set the value in memory address `address` to `value`.
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::Cpu;
///
/// let mut cpu = Cpu::with_memory(vec![1, 0, 0, 0, 99]);
/// cpu.set(0, 2);
/// assert_eq!(cpu.get(0), 2);
/// ````
pub fn set(&mut self, address: usize, value: RegVal) {
let len = self.memory.len();
if address >= len {
self.memory.append(&mut vec![0; address+1-len]);
}
self.memory[address] = value;
}
/// Sets the input to `input`. This will be read by certain commands, like opcode 3
/// (set_to_input).
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::{Cpu, OutputState};
///
/// // Store input (opcode 3) register 4. Then Output (opcode 104) register 3 and Halt (opcode 99).
/// let mut cpu = Cpu::with_memory(vec![3, 3, 104, 0, 99]);
/// cpu.set_input(42);
/// assert_eq!(cpu.run(), OutputState::DiagnosticCode(42));
/// ````
pub fn set_input(&mut self, input: RegVal) {
self.input.push_back(input);
}
/// Get the value from memory address `address`.
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::Cpu;
///
/// let mut cpu = Cpu::with_memory(vec![1, 0, 0, 0, 99]);
/// assert_eq!(cpu.get(0), 1);
/// ````
pub fn get(&self, address: usize) -> RegVal {
if address < self.memory.len() {
self.memory[address]
} else {
0
}
}
/// Add val_1 and val_2 and store the result in address dest.
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::{Cpu, OutputState};
///
/// let mut cpu = Cpu::with_memory(vec![
/// 1, 20, 21, 17, // Add values from addresses 20 and 21 (23 + 42) and write to 17.
/// 101, 23, 20, 18, // Add 23 and value from 20 (23 + 23) and write to 18.
/// 1001, 21, 42, 19, // Add value from 21 and literal 42 (42 + 42) and write to 19.
/// 1101, 100, -1, 16, // Add literals 100 and -1 and write to 16.
/// 0, 0, 0, 0, 23, 42]); // 16-21: Output destinations and inputs.
/// assert_eq!(cpu.run(), OutputState::Halt);
/// assert_eq!(cpu.get(17), 65);
/// assert_eq!(cpu.get(18), 46);
/// assert_eq!(cpu.get(19), 84);
/// ````
fn add(&mut self, reg_1: usize, reg_2: usize, dest: usize) {
// eprintln!("{val_1}+{val_2}={}", val_1+val_2);
self.set(dest, self.get(reg_1) + self.get(reg_2));
self.instr_ptr += 4;
}
/// Multiply val_1 and val_2 and store the result in dest.
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::{Cpu, OutputState};
///
/// let mut cpu = Cpu::with_memory(vec![
/// 2, 20, 21, 17, // Multiply values from addresses 20 and 21 (23 * 42) and write to 17.
/// 102, 23, 20, 18, // Multiply 23 and value from 20 (23 * 23) and write to 18.
/// 1002, 21, 42, 19, // Multiply value from 21 and literal 42 (42 * 42) and write to 19.
/// 1102, -33, -3, 16, // Multiply literals -33 and -3 and write to 16.
/// 0, 0, 0, 0, 23, 42]); // 16-21: Output destinations and inputs.
/// assert_eq!(cpu.run(), OutputState::Halt);
/// assert_eq!(cpu.get(17), 966);
/// assert_eq!(cpu.get(18), 529);
/// assert_eq!(cpu.get(19), 1764);
/// ````
fn mul(&mut self, reg_1: usize, reg_2: usize, dest: usize) {
self.set(dest, self.get(reg_1) * self.get(reg_2));
self.instr_ptr += 4;
}
/// Store the first element of `input` at address `dest`.
///
/// ## Panics
/// Panics if dest points outside the memory or input is empty.
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::Cpu;
///
/// // Store input (opcode 3) in register 4. Then Halt (opcode 99).
/// let mut cpu = Cpu::with_memory(vec![3, 4, 99, 0, 0]);
/// cpu.set_input(42);
/// cpu.run();
/// assert_eq!(cpu.get(4), 42);
/// ````
fn set_to_input(&mut self, dest: usize) {
let input = self.input.pop_front().expect("Input is empty");
self.set(dest, input);
self.instr_ptr += 2;
}
/// Return DiagnosticCode(val) if the next instruction is Halt (opcode 99) and Output(val) otherwise.
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::{Cpu, OutputState};
///
/// // Return (opcode 4) the value from memory address 3. Then Halt (opcode 99).
/// let mut cpu = Cpu::with_memory(vec![104, 42, 99]);
/// assert_eq!(cpu.run(), OutputState::DiagnosticCode(42));
/// ````
fn ret(&mut self, reg: usize) -> OutputState {
self.instr_ptr += 2;
if self.get(self.instr_ptr) == 99 {
OutputState::DiagnosticCode(self.get(reg))
} else {
OutputState::Output(self.get(reg))
}
}
/// Jump to address `dest_val`, if `val` is non-zero.
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::{Cpu, OutputState};
///
/// let mut cpu = Cpu::with_memory(vec![
/// 1005, 0, 4, // jump to 4 if address 0 holds a non-zero value (it does, neamely 1005).
/// 99, // instruction 3: Skipped due to first jump
/// 1105, 0, 10,// jump to 10 if 0 is non-zero (it isn't, so we continue)
/// 104, 1, // return literal 1
/// 99, // halt (this is where we actually halt)
/// 104, 2, // instruction 10: This would be our jump target if we didn't jump from 4; return literal 2.
/// 99, // halt
/// ]);
/// assert_eq!(cpu.run(), OutputState::DiagnosticCode(1));
/// ````
fn jnz(&mut self, reg: usize, dest_reg: usize) {
if self.get(reg) != 0 {
self.instr_ptr = self.get(dest_reg) as usize;
} else {
self.instr_ptr += 3;
}
}
/// Jump to address `dest_val`, if `val` is zero.
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::{Cpu, OutputState};
///
/// let mut cpu = Cpu::with_memory(vec![
/// 1106, 0, 4, // jump to 4 if 0 is zero (it is).
/// 99, // instruction 3: Skipped due to first jump
/// 1006, 0, 10,// jump to 10 if address 0 holds a zero (it doesn't, so we continue)
/// 104, 1, // return literal 1
/// 99, // halt (this is where we actually halt)
/// 104, 2, // instruction 10: This would be our jump target if we didn't jump from 4; return literal 2.
/// 99, // halt
/// ]);
/// assert_eq!(cpu.run(), OutputState::DiagnosticCode(1));
/// ````
fn jiz(&mut self, reg: usize, dest_reg: usize) {
if self.get(reg) == 0 {
self.instr_ptr = self.get(dest_reg) as usize;
} else {
self.instr_ptr += 3;
}
}
/// Set address `dest` to `1` if `val_1` is lower value than `val_2` and
/// to `0` otherwise.
///
/// ## Panics
/// Panics if any of the addresses point outside the memory.
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::Cpu;
///
/// let mut cpu = Cpu::with_memory(vec![
/// 1107, 23, 42, 3, // Set address 3 to 1, since 23 < 42
/// 1107, 42, 23, 7, // Set address 7 to 0, since !(42<23)
/// 7, 1, 1, 11, // Set address 11 to 0, since address 1 doesn't hold a lower value than itself !(23<23)
/// 99, // Halt
/// ]);
///
/// cpu.run();
/// assert_eq!(cpu.get(3), 1);
/// assert_eq!(cpu.get(7), 0);
/// assert_eq!(cpu.get(11), 0);
/// ````
fn lt(&mut self, reg_1: usize, reg_2: usize, dest: usize) {
self.set(dest,
if self.get(reg_1) < self.get(reg_2) {
1
} else {
0
});
self.instr_ptr += 4;
}
/// Set address `dest` to `1` if values `val_1` and `val_2` are equal
/// does and to `0` otherwise.
///
/// ## Panics
/// Panics if any of the addresses point outside the memory.
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::Cpu;
///
/// let mut cpu = Cpu::with_memory(vec![
/// 1108, 23, 23, 3,// Set address 3 to 1, since 23 == 23
/// 1108, 23, 42, 7,// Set address 7 to 0, since (23 != 42)
/// 8, 3, 7, 11, // Set address 11 to 0, since 3 and 7 hold different values (1 != 0)
/// 99, // Halt
/// 23, 42]); // data storage (addresses 9 and 10)
///
/// cpu.run();
/// assert_eq!(cpu.get(3), 1);
/// assert_eq!(cpu.get(7), 0);
/// assert_eq!(cpu.get(11), 0);
/// ````
fn eq(&mut self, reg_1: usize, reg_2: usize, dest: usize) {
self.set(dest,
if self.get(reg_1) == self.get(reg_2) {
1
} else {
0
});
self.instr_ptr += 4;
}
/// Adjust the relative base pointer by the given amount.
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::{Cpu, OutputState};
///
/// let mut cpu = Cpu::with_memory(vec![
/// 109, -2, // Decrease relative base by 1 (to -2),
/// 204, 4, // Output the contents of address (relative base + 4 == 2)
/// 99 // Halt
/// ]);
///
/// assert_eq!(cpu.run(), OutputState::DiagnosticCode(204));
/// ````
fn adj_rel_base(&mut self, reg: usize) {
self.rel_base += self.get(reg);
self.instr_ptr += 2;
}
fn pos(&self, offset: usize) -> usize {
self.get(self.instr_ptr + offset) as usize
}
fn imm(&self, offset: usize) -> usize {
self.instr_ptr + offset
}
fn rel(&self, offset: usize) -> usize {
(self.rel_base + self.get(self.instr_ptr + offset)) as usize
}
/// Run the program from current memory, starting at `instr_ptr` and running until opcode
/// 99 (Halt) is encountered.
///
/// Returns DiagnosticCode(val) if the program encountered some output instruction immediately followed
/// by a Halt (opcode 99), Halt if it ended on Halt without encountering an output
/// instruction, or Output(val) on an output instruction that is not followed by a Halt.
///
/// ## Panics
/// Throws a `panic` whenever an undefined opcode is encountered at `instr_ptr`, or if the
/// program is trying to access an address outside the memory.
///
/// ## Example
/// ````
/// use intcode_processor::intcode_processor::{Cpu, OutputState};
///
/// // Add (opcode 1) registers 5 (23) and 6 (42) and store into 0. Then Halt (opcode 99).
/// let mut cpu = Cpu::with_memory(vec![1, 5, 6, 0, 99, 23, 42]);
/// let result = cpu.run();
/// assert_eq!(result, OutputState::Halt);
/// assert_eq!(cpu.get(0), 65);
/// ````
pub fn run(&mut self) -> OutputState {
loop {
let instruction = self.memory[self.instr_ptr];
// eprintln!("{}: {}", self.instr_ptr, instruction);
let params: Vec<usize> = (1..=3).map(|i| match (instruction/(10_isize.pow(i+1)))%10 {
0 => self.pos(i as usize),
1 => self.imm(i as usize),
2 => self.rel(i as usize),
e => panic!("Unexpected mode: {e}"),
}).collect();
match instruction % 100 {
1 => self.add(params[0], params[1], params[2]),
2 => self.mul(params[0], params[1], params[2]),
3 => self.set_to_input(params[0]),
4 => return self.ret(params[0]),
5 => self.jnz(params[0], params[1]),
6 => self.jiz(params[0], params[1]),
7 => self.lt(params[0], params[1], params[2]),
8 => self.eq(params[0], params[1], params[2]),
9 => self.adj_rel_base(params[0]),
99 => return OutputState::Halt,
_ => panic!("Unexpected instruction: {}", instruction),
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::intcode_processor::*;
#[test]
fn day02_1() {
let mem = vec![1,12,2,3,2,3,11,0,99,30,40,20,173,984523];
let mut cpu = Cpu::with_memory(mem);
assert_eq!(cpu.run(), OutputState::Halt);
assert_eq!(cpu.get(0), 3500);
}
#[test]
fn day02_2() {
let mem = vec![1,2,13,3,2,3,11,0,99,30,40,20,173,984523];
let mut cpu = Cpu::with_memory(mem);
assert_eq!(cpu.run(), OutputState::Halt);
assert_eq!(cpu.get(0), 19690720);
}
#[test]
fn day05() {
let mem = vec![3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99];
for input in 0..=10 {
let mut cpu = Cpu::with_memory(mem.to_vec());
cpu.set_input(input);
let expected = match input {
l if l < 8 => 999,
8 => 1000,
g if g > 8 => 1001,
_ => unreachable!(),
};
assert_eq!(cpu.run(), OutputState::Output(expected));
}
}
#[test]
fn day07() {
let template = Cpu::try_with_memory_from_str("3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33,1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0").unwrap();
let perm = [0, 1, 2, 3, 4];
let mut output = 0;
for input in perm.iter() {
let mut cpu = template.clone();
cpu.set_input(*input);
cpu.set_input(output);
loop {
match cpu.run() {
OutputState::DiagnosticCode(out) => {
output = out;
break;
},
OutputState::Output(e) => cpu.set_input(e),
OutputState::Halt => break,
}
}
}
assert_eq!(output, 56012);
}
#[test]
fn rel_mode() {
let mem = [109, 10, 204, -7, 99];
let mut cpu = Cpu::with_memory(mem.to_vec());
assert_eq!(cpu.run(), OutputState::DiagnosticCode(-7));
}
#[test]
fn day09_sample() {
let mem = [109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99];
let mut cpu = Cpu::with_memory(mem.to_vec());
for res in mem {
assert_eq!(cpu.run(), OutputState::Output(res));
}
}
#[test]
fn day09() {
let mut cpu = Cpu::with_memory(vec![1102,34463338,34463338,63,1007,63,34463338,63,1005,63,53,1101,3,0,1000,109,988,209,12,9,1000,209,6,209,3,203,0,1008,1000,1,63,1005,63,65,1008,1000,2,63,1005,63,904,1008,1000,0,63,1005,63,58,4,25,104,0,99,4,0,104,0,99,4,17,104,0,99,0,0,1102,1,37,1000,1101,856,0,1029,1101,286,0,1025,1101,39,0,1004,1101,861,0,1028,1101,845,0,1026,1102,28,1,1002,1102,1,0,1020,1101,0,892,1023,1101,0,291,1024,1101,35,0,1018,1101,0,27,1006,1102,1,26,1011,1101,33,0,1019,1102,31,1,1014,1102,1,36,1010,1102,23,1,1007,1101,0,32,1016,1101,29,0,1008,1101,20,0,1001,1102,1,25,1015,1101,38,0,1017,1101,0,24,1012,1102,1,22,1005,1101,1,0,1021,1101,0,21,1003,1102,1,838,1027,1102,1,30,1013,1101,895,0,1022,1101,0,34,1009,109,7,1208,0,22,63,1005,63,201,1001,64,1,64,1105,1,203,4,187,1002,64,2,64,109,-6,2102,1,5,63,1008,63,24,63,1005,63,223,1105,1,229,4,209,1001,64,1,64,1002,64,2,64,109,17,21102,40,1,-6,1008,1012,40,63,1005,63,255,4,235,1001,64,1,64,1106,0,255,1002,64,2,64,109,-15,21108,41,41,9,1005,1012,277,4,261,1001,64,1,64,1106,0,277,1002,64,2,64,109,11,2105,1,10,4,283,1105,1,295,1001,64,1,64,1002,64,2,64,109,-9,21101,42,0,8,1008,1013,44,63,1005,63,315,1105,1,321,4,301,1001,64,1,64,1002,64,2,64,109,13,1206,3,337,1001,64,1,64,1106,0,339,4,327,1002,64,2,64,109,-10,1208,0,29,63,1005,63,361,4,345,1001,64,1,64,1106,0,361,1002,64,2,64,109,2,2108,27,-4,63,1005,63,383,4,367,1001,64,1,64,1105,1,383,1002,64,2,64,109,-4,1207,2,30,63,1005,63,405,4,389,1001,64,1,64,1105,1,405,1002,64,2,64,109,22,1205,-8,417,1106,0,423,4,411,1001,64,1,64,1002,64,2,64,109,-27,2108,19,0,63,1005,63,443,1001,64,1,64,1106,0,445,4,429,1002,64,2,64,109,13,21108,43,45,-1,1005,1013,461,1106,0,467,4,451,1001,64,1,64,1002,64,2,64,109,1,21107,44,45,4,1005,1019,485,4,473,1105,1,489,1001,64,1,64,1002,64,2,64,109,-8,2102,1,-7,63,1008,63,37,63,1005,63,515,4,495,1001,64,1,64,1106,0,515,1002,64,2,64,109,1,2107,38,-4,63,1005,63,533,4,521,1105,1,537,1001,64,1,64,1002,64,2,64,109,4,21107,45,44,1,1005,1013,553,1106,0,559,4,543,1001,64,1,64,1002,64,2,64,109,-7,2107,21,-4,63,1005,63,575,1106,0,581,4,565,1001,64,1,64,1002,64,2,64,109,9,1205,7,599,4,587,1001,64,1,64,1105,1,599,1002,64,2,64,109,-11,2101,0,-3,63,1008,63,40,63,1005,63,619,1105,1,625,4,605,1001,64,1,64,1002,64,2,64,109,1,2101,0,-2,63,1008,63,28,63,1005,63,651,4,631,1001,64,1,64,1106,0,651,1002,64,2,64,109,1,21102,46,1,7,1008,1012,44,63,1005,63,671,1106,0,677,4,657,1001,64,1,64,1002,64,2,64,109,4,1201,-7,0,63,1008,63,28,63,1005,63,699,4,683,1105,1,703,1001,64,1,64,1002,64,2,64,109,-6,1207,-3,36,63,1005,63,719,1105,1,725,4,709,1001,64,1,64,1002,64,2,64,109,-4,1201,6,0,63,1008,63,23,63,1005,63,745,1106,0,751,4,731,1001,64,1,64,1002,64,2,64,109,8,1202,-6,1,63,1008,63,20,63,1005,63,777,4,757,1001,64,1,64,1105,1,777,1002,64,2,64,109,5,1202,-5,1,63,1008,63,25,63,1005,63,801,1001,64,1,64,1105,1,803,4,783,1002,64,2,64,109,8,21101,47,0,-6,1008,1014,47,63,1005,63,829,4,809,1001,64,1,64,1106,0,829,1002,64,2,64,109,1,2106,0,6,1001,64,1,64,1106,0,847,4,835,1002,64,2,64,109,11,2106,0,-4,4,853,1105,1,865,1001,64,1,64,1002,64,2,64,109,-15,1206,3,883,4,871,1001,64,1,64,1106,0,883,1002,64,2,64,109,14,2105,1,-8,1105,1,901,4,889,1001,64,1,64,4,64,99,21102,1,27,1,21102,1,915,0,1106,0,922,21201,1,57564,1,204,1,99,109,3,1207,-2,3,63,1005,63,964,21201,-2,-1,1,21102,1,942,0,1105,1,922,22101,0,1,-1,21201,-2,-3,1,21101,957,0,0,1105,1,922,22201,1,-1,-2,1106,0,968,21202,-2,1,-2,109,-3,2106,0,0]);
cpu.set_input(1);
assert_eq!(cpu.run(), OutputState::DiagnosticCode(2316632620));
}
}

View file

@ -0,0 +1,8 @@
[package]
name = "day01_the_tyranny_of_the_rocket_equation"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,45 @@
Santa has become stranded at the edge of the Solar System while delivering presents to other planets! To accurately calculate his position in space, safely align his warp drive, and return to Earth in time to save Christmas, he needs you to bring him measurements from *fifty stars*.
Collect stars by solving puzzles. Two puzzles will be made available on each day in the Advent calendar; the second puzzle is unlocked when you complete the first. Each puzzle grants *one star*. Good luck!
The Elves quickly load you into a spacecraft and prepare to launch.
At the first Go / No Go poll, every Elf is Go until the Fuel Counter-Upper. They haven't determined the amount of fuel required yet.
Fuel required to launch a given *module* is based on its *mass*. Specifically, to find the fuel required for a module, take its mass, divide by three, round down, and subtract 2.
For example:
* For a mass of `12`, divide by 3 and round down to get `4`, then subtract 2 to get `2`.
* For a mass of `14`, dividing by 3 and rounding down still yields `4`, so the fuel required is also `2`.
* For a mass of `1969`, the fuel required is `654`.
* For a mass of `100756`, the fuel required is `33583`.
The Fuel Counter-Upper needs to know the total fuel requirement. To find it, individually calculate the fuel needed for the mass of each module (your puzzle input), then add together all the fuel values.
*What is the sum of the fuel requirements* for all of the modules on your spacecraft?
Your puzzle answer was `3363033`.
\--- Part Two ---
----------
During the second Go / No Go poll, the Elf in charge of the Rocket Equation Double-Checker stops the launch sequence. Apparently, you forgot to include additional fuel for the fuel you just added.
Fuel itself requires fuel just like a module - take its mass, divide by three, round down, and subtract 2. However, that fuel *also* requires fuel, and *that* fuel requires fuel, and so on. Any mass that would require *negative fuel* should instead be treated as if it requires *zero fuel*; the remaining mass, if any, is instead handled by *wishing really hard*, which has no mass and is outside the scope of this calculation.
So, for each module mass, calculate its fuel and add it to the total. Then, treat the fuel amount you just calculated as the input mass and repeat the process, continuing until a fuel requirement is zero or negative. For example:
* A module of mass `14` requires `2` fuel. This fuel requires no further fuel (2 divided by 3 and rounded down is `0`, which would call for a negative fuel), so the total fuel required is still just `2`.
* At first, a module of mass `1969` requires `654` fuel. Then, this fuel requires `216` more fuel (`654 / 3 - 2`). `216` then requires `70` more fuel, which requires `21` fuel, which requires `5` fuel, which requires no further fuel. So, the total fuel required for a module of mass `1969` is `654 + 216 + 70 + 21 + 5 = 966`.
* The fuel required by a module of mass `100756` and its fuel is: `33583 + 11192 + 3728 + 1240 + 411 + 135 + 43 + 12 + 2 = 50346`.
*What is the sum of the fuel requirements* for all of the modules on your spacecraft when also taking into account the mass of the added fuel? (Calculate the fuel requirements for each module separately, then add them all up at the end.)
Your puzzle answer was `5041680`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
If you still want to see it, you can [get your puzzle input](1/input).

View file

@ -0,0 +1,36 @@
pub fn run(input: &str) -> (usize, usize) {
let first = input.lines().map(|line| line.parse::<usize>().unwrap() / 3 -2).sum();
let second = input.lines().map(|line| {
let weight = line.parse::<usize>().unwrap();
let mut total = (weight/3).saturating_sub(2);
let mut next = (total/3).saturating_sub(2);
while next > 0 {
total += next;
next = (next/3).saturating_sub(2);
}
total
}).sum::<usize>();
(first, second)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_sample() {
let sample_input = read_file("tests/sample_input");
assert_eq!(run(&sample_input), (34241, 51316));
}
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (3363033, 5041680));
}
}

View file

@ -0,0 +1,100 @@
120588
137636
114877
118328
97394
58497
139343
80307
125063
70956
119676
76115
91916
64618
82881
57000
141785
73460
68992
125701
97839
137800
111051
104591
114396
60210
80238
112009
70265
140582
58765
96848
130438
55615
53903
109361
129512
75888
93231
54697
125320
53614
87173
71762
147739
131840
123979
54434
121517
113518
83544
124924
76608
130483
149285
134147
111589
88174
136392
94448
139244
54064
85110
102985
95646
54649
129755
135795
119653
147633
108386
143180
126587
119273
130579
56006
83232
99948
147711
83092
99706
98697
143231
94526
53102
86002
71413
111054
147220
136504
59308
61123
148771
113986
55483
94426
62791
100959
63604
112511

View file

@ -0,0 +1,4 @@
12
14
1969
100756

View file

@ -0,0 +1,9 @@
[package]
name = "day02_1202_program_alarm"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
intcode_processor = { path = "../common/intcode_processor" }

View file

@ -0,0 +1,91 @@
On the way to your [gravity assist](https://en.wikipedia.org/wiki/Gravity_assist) around the Moon, your ship computer beeps angrily about a "[1202 program alarm](https://www.hq.nasa.gov/alsj/a11/a11.landing.html#1023832)". On the radio, an Elf is already explaining how to handle the situation: "Don't worry, that's perfectly norma--" The ship computer [bursts into flames](https://en.wikipedia.org/wiki/Halt_and_Catch_Fire).
You notify the Elves that the computer's [magic smoke](https://en.wikipedia.org/wiki/Magic_smoke) seems to have escaped. "That computer ran *Intcode* programs like the gravity assist program it was working on; surely there are enough spare parts up there to build a new Intcode computer!"
An Intcode program is a list of [integers](https://en.wikipedia.org/wiki/Integer) separated by commas (like `1,0,0,3,99`). To run one, start by looking at the first integer (called position `0`). Here, you will find an *opcode* - either `1`, `2`, or `99`. The opcode indicates what to do; for example, `99` means that the program is finished and should immediately halt. Encountering an unknown opcode means something went wrong.
Opcode `1` *adds* together numbers read from two positions and stores the result in a third position. The three integers *immediately after* the opcode tell you these three positions - the first two indicate the *positions* from which you should read the input values, and the third indicates the *position* at which the output should be stored.
For example, if your Intcode computer encounters `1,10,20,30`, it should read the values at positions `10` and `20`, add those values, and then overwrite the value at position `30` with their sum.
Opcode `2` works exactly like opcode `1`, except it *multiplies* the two inputs instead of adding them. Again, the three integers after the opcode indicate *where* the inputs and outputs are, not their values.
Once you're done processing an opcode, *move to the next one* by stepping forward `4` positions.
For example, suppose you have the following program:
```
1,9,10,3,2,3,11,0,99,30,40,50
```
For the purposes of illustration, here is the same program split into multiple lines:
```
1,9,10,3,
2,3,11,0,
99,
30,40,50
```
The first four integers, `1,9,10,3`, are at positions `0`, `1`, `2`, and `3`. Together, they represent the first opcode (`1`, addition), the positions of the two inputs (`9` and `10`), and the position of the output (`3`). To handle this opcode, you first need to get the values at the input positions: position `9` contains `30`, and position `10` contains `40`. *Add* these numbers together to get `70`. Then, store this value at the output position; here, the output position (`3`) is *at* position `3`, so it overwrites itself. Afterward, the program looks like this:
```
1,9,10,70,
2,3,11,0,
99,
30,40,50
```
Step forward `4` positions to reach the next opcode, `2`. This opcode works just like the previous, but it multiplies instead of adding. The inputs are at positions `3` and `11`; these positions contain `70` and `50` respectively. Multiplying these produces `3500`; this is stored at position `0`:
```
3500,9,10,70,
2,3,11,0,
99,
30,40,50
```
Stepping forward `4` more positions arrives at opcode `99`, halting the program.
Here are the initial and final states of a few more small programs:
* `1,0,0,0,99` becomes `*2*,0,0,0,99` (`1 + 1 = 2`).
* `2,3,0,3,99` becomes `2,3,0,*6*,99` (`3 * 2 = 6`).
* `2,4,4,5,99,0` becomes `2,4,4,5,99,*9801*` (`99 * 99 = 9801`).
* `1,1,1,4,99,5,6,0,99` becomes `*30*,1,1,4,*2*,5,6,0,99`.
Once you have a working computer, the first step is to restore the gravity assist program (your puzzle input) to the "1202 program alarm" state it had just before the last computer caught fire. To do this, *before running the program*, replace position `1` with the value `12` and replace position `2` with the value `2`. *What value is left at position `0`* after the program halts?
Your puzzle answer was `3760627`.
\--- Part Two ---
----------
"Good, the new computer seems to be working correctly! *Keep it nearby* during this mission - you'll probably use it again. Real Intcode computers support many more features than your new one, but we'll let you know what they are as you need them."
"However, your current priority should be to complete your gravity assist around the Moon. For this mission to succeed, we should settle on some terminology for the parts you've already built."
Intcode programs are given as a list of integers; these values are used as the initial state for the computer's *memory*. When you run an Intcode program, make sure to start by initializing memory to the program's values. A position in memory is called an *address* (for example, the first value in memory is at "address 0").
Opcodes (like `1`, `2`, or `99`) mark the beginning of an *instruction*. The values used immediately after an opcode, if any, are called the instruction's *parameters*. For example, in the instruction `1,2,3,4`, `1` is the opcode; `2`, `3`, and `4` are the parameters. The instruction `99` contains only an opcode and has no parameters.
The address of the current instruction is called the *instruction pointer*; it starts at `0`. After an instruction finishes, the instruction pointer increases by *the number of values in the instruction*; until you add more instructions to the computer, this is always `4` (`1` opcode + `3` parameters) for the add and multiply instructions. (The halt instruction would increase the instruction pointer by `1`, but it halts the program instead.)
"With terminology out of the way, we're ready to proceed. To complete the gravity assist, you need to *determine what pair of inputs produces the output `19690720`*."
The inputs should still be provided to the program by replacing the values at addresses `1` and `2`, just like before. In this program, the value placed in address `1` is called the *noun*, and the value placed in address `2` is called the *verb*. Each of the two input values will be between `0` and `99`, inclusive.
Once the program has halted, its output is available at address `0`, also just like before. Each time you try a pair of inputs, make sure you first *reset the computer's memory to the values in the program* (your puzzle input) - in other words, don't reuse memory from a previous attempt.
Find the input *noun* and *verb* that cause the program to produce the output `19690720`. *What is `100 * noun + verb`?* (For example, if `noun=12` and `verb=2`, the answer would be `1202`.)
Your puzzle answer was `7195`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
If you still want to see it, you can [get your puzzle input](2/input).

View file

@ -0,0 +1,47 @@
use intcode_processor::intcode_processor::Cpu;
pub fn run(input: &str) -> (isize, isize) {
let memory = input.split(',').map(|s| s.parse::<isize>().unwrap()).collect();
let cpu = Cpu::with_memory(memory);
let mut cpu_1 = cpu.clone();
cpu_1.set(1, 12);
cpu_1.set(2, 2);
cpu_1.run();
let first = cpu_1.get(0);
let mut second = 0;
'outer: for noun in 0..100 {
for verb in 0..100 {
let mut cpu_2 = cpu.clone();
cpu_2.set(1, noun);
cpu_2.set(2, verb);
cpu_2.run();
if cpu_2.get(0) == 19690720 {
second = 100 * noun + verb;
break 'outer;
}
}
}
(first, second)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_sample() {
let sample_input = read_file("tests/sample_input");
assert_eq!(run(&sample_input), (3500, 213));
}
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (3760627, 7195));
}
}

View file

@ -0,0 +1 @@
1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,1,9,19,1,10,19,23,2,9,23,27,1,6,27,31,2,31,9,35,1,5,35,39,1,10,39,43,1,10,43,47,2,13,47,51,1,10,51,55,2,55,10,59,1,9,59,63,2,6,63,67,1,5,67,71,1,71,5,75,1,5,75,79,2,79,13,83,1,83,5,87,2,6,87,91,1,5,91,95,1,95,9,99,1,99,6,103,1,103,13,107,1,107,5,111,2,111,13,115,1,115,6,119,1,6,119,123,2,123,13,127,1,10,127,131,1,131,2,135,1,135,5,0,99,2,14,0,0

View file

@ -0,0 +1 @@
1,9,10,3,2,3,11,0,99,30,40,20,173,984523,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

View file

@ -0,0 +1,8 @@
[package]
name = "day03_crossed_wires"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,94 @@
The gravity assist was successful, and you're well on your way to the Venus refuelling station. During the rush back on Earth, the fuel management system wasn't completely installed, so that's next on the priority list.
Opening the front panel reveals a jumble of wires. Specifically, *two wires* are connected to a central port and extend outward on a grid. You trace the path each wire takes as it leaves the central port, one wire per line of text (your puzzle input).
The wires twist and turn, but the two wires occasionally cross paths. To fix the circuit, you need to *find the intersection point closest to the central port*. Because the wires are on a grid, use the [Manhattan distance](https://en.wikipedia.org/wiki/Taxicab_geometry) for this measurement. While the wires do technically cross right at the central port where they both start, this point does not count, nor does a wire count as crossing with itself.
For example, if the first wire's path is `R8,U5,L5,D3`, then starting from the central port (`o`), it goes right `8`, up `5`, left `5`, and finally down `3`:
```
...........
...........
...........
....+----+.
....|....|.
....|....|.
....|....|.
.........|.
.o-------+.
...........
```
Then, if the second wire's path is `U7,R6,D4,L4`, it goes up `7`, right `6`, down `4`, and left `4`:
```
...........
.+-----+...
.|.....|...
.|..+--X-+.
.|..|..|.|.
.|.-X--+.|.
.|..|....|.
.|.......|.
.o-------+.
...........
```
These wires cross at two locations (marked `X`), but the lower-left one is closer to the central port: its distance is `3 + 3 = 6`.
Here are a few more examples:
* `R75,D30,R83,U83,L12,D49,R71,U7,L72
U62,R66,U55,R34,D71,R55,D58,R83` = distance `159`
* `R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51
U98,R91,D20,R16,D67,R40,U7,R15,U6,R7` = distance `135`
*What is the Manhattan distance* from the central port to the closest intersection?
Your puzzle answer was `1225`.
\--- Part Two ---
----------
It turns out that this circuit is very timing-sensitive; you actually need to *minimize the signal delay*.
To do this, calculate the *number of steps* each wire takes to reach each intersection; choose the intersection where the *sum of both wires' steps* is lowest. If a wire visits a position on the grid multiple times, use the steps value from the *first* time it visits that position when calculating the total value of a specific intersection.
The number of steps a wire takes is the total number of grid squares the wire has entered to get to that location, including the intersection being considered. Again consider the example from above:
```
...........
.+-----+...
.|.....|...
.|..+--X-+.
.|..|..|.|.
.|.-X--+.|.
.|..|....|.
.|.......|.
.o-------+.
...........
```
In the above example, the intersection closest to the central port is reached after `8+5+5+2 = *20*` steps by the first wire and `7+6+4+3 = *20*` steps by the second wire for a total of `20+20 = *40*` steps.
However, the top-right intersection is better: the first wire takes only `8+5+2 = *15*` and the second wire takes only `7+6+2 = *15*`, a total of `15+15 = *30*` steps.
Here are the best steps for the extra examples from above:
* `R75,D30,R83,U83,L12,D49,R71,U7,L72
U62,R66,U55,R34,D71,R55,D58,R83` = `610` steps
* `R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51
U98,R91,D20,R16,D67,R40,U7,R15,U6,R7` = `410` steps
*What is the fewest combined steps the wires must take to reach an intersection?*
Your puzzle answer was `107036`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
If you still want to see it, you can [get your puzzle input](3/input).

View file

@ -0,0 +1,107 @@
struct Line {
bottom_left: (isize, isize),
top_right: (isize, isize),
}
impl Line {
fn intersection(&self, other: &Self) -> Option<(isize, isize)> {
if self.bottom_left.0 <= other.bottom_left.0 && self.top_right.0 >= other.top_right.0 && other.bottom_left.1 <= self.bottom_left.1 && other.top_right.1 >= self.top_right.1 {
Some((other.bottom_left.0, self.bottom_left.1))
} else if other.bottom_left.0 <= self.bottom_left.0 && other.top_right.0 >= self.top_right.0 && self.bottom_left.1 <= other.bottom_left.1 && self.top_right.1 >= other.top_right.1 {
Some((self.bottom_left.0, other.bottom_left.1))
} else if self.bottom_left == other.top_right {
Some(self.bottom_left)
} else if self.top_right == other.bottom_left {
Some(self.top_right)
} else {
None
}
}
}
pub fn run(input: &str) -> (usize, usize) {
let mut lines = input.lines();
let wire1 = lines_from(lines.next().unwrap());
let intersections = intersections_by_applying(lines.next().unwrap(), &wire1);
let first = intersections.iter().map(|(coords, _len2)| coords.0.unsigned_abs() + coords.1.unsigned_abs()).min().unwrap();
let second = intersections.iter().map(|(coords, len2)| len2 + path_len(&wire1, *coords)).min().unwrap();
(first, second)
}
fn path_len(wire: &[Line], (x, y): (isize, isize)) -> usize {
let mut len = 0;
let mut current = (0, 0);
for segment in wire {
if (segment.bottom_left.0..=segment.top_right.0).contains(&x) && (segment.bottom_left.1..=segment.top_right.1).contains(&y) {
return len + x.abs_diff(current.0) + y.abs_diff(current.1);
} else {
len += segment.bottom_left.0.abs_diff(segment.top_right.0) + segment.bottom_left.1.abs_diff(segment.top_right.1);
current = if current == segment.bottom_left { segment.top_right } else { segment.bottom_left };
}
}
len
}
fn intersections_by_applying(val: &str, existing_wire: &[Line]) -> Vec<((isize, isize), usize)> {
let mut len=0;
let mut current = (0, 0);
val.split(',')
.flat_map(|segment| {
let (this, dist) = segment_from(segment, &mut current);
len += dist;
existing_wire.iter()
.filter_map(|other| this.intersection(other))
.map(|intersection| (intersection, len - intersection.0.abs_diff(current.0) - intersection.1.abs_diff(current.1)))
.collect::<Vec<((isize, isize), usize)>>()
// Skip the first intersection (at the origin)
}).skip(1)
.collect()
}
fn lines_from(val: &str) -> Vec<Line> {
let mut current = (0, 0);
val.split(',').map(|segment| segment_from(segment, &mut current).0).collect()
}
fn segment_from(val: &str, current: &mut (isize, isize)) -> (Line, usize) {
let dir = val.chars().next().unwrap();
let count: String = val.chars().skip(1).collect();
let dist = count.parse::<isize>().unwrap();
let dest = match dir {
'U' => (current.0, current.1 - dist),
'D' => (current.0, current.1 + dist),
'L' => (current.0 - dist, current.1),
'R' => (current.0 + dist, current.1),
_ => panic!("Unexpected Direction: {dir}"),
};
let res = Line {
bottom_left: (current.0.min(dest.0), current.1.min(dest.1)),
top_right: (current.0.max(dest.0), current.1.max(dest.1)),
};
*current = dest;
(res, dist as usize)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_sample() {
let sample_input = read_file("tests/sample_input");
assert_eq!(run(&sample_input), (159, 610));
}
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (1225, 107036));
}
}

View file

@ -0,0 +1,2 @@
R1003,U756,L776,U308,R718,D577,R902,D776,R760,U638,R289,D70,L885,U161,R807,D842,R175,D955,R643,U380,R329,U573,L944,D2,L807,D886,L549,U592,R152,D884,L761,D915,L726,D677,L417,D651,L108,D377,L699,D938,R555,D222,L689,D196,L454,U309,L470,D234,R198,U689,L996,U117,R208,D310,R572,D562,L207,U244,L769,U186,R153,D756,R97,D625,R686,U244,R348,U586,L385,D466,R483,U718,L892,D39,R692,U756,L724,U148,R70,U224,L837,D370,L309,U235,R382,D579,R404,D146,R6,U584,L840,D863,R942,U646,R146,D618,L12,U210,R126,U163,R931,D661,L710,D883,L686,D688,L148,D19,R703,U530,R889,U186,R779,D503,R417,U272,R541,U21,L562,D10,L349,U998,R69,D65,R560,D585,L949,D372,L110,D865,R212,U56,L936,U957,L88,U612,R927,U642,R416,U348,L541,D416,L808,D759,R449,D6,L517,D4,R494,D143,L536,U341,R394,U179,L22,D680,L138,U249,L285,U879,L717,U756,L313,U222,R823,D208,L134,U984,R282,U635,R207,D63,L416,U511,L179,D582,L651,U932,R646,U378,R263,U138,L920,U523,L859,D556,L277,D518,R489,U561,L457,D297,R72,U920,L583,U23,L395,D844,R776,D552,L55,D500,R111,U409,R685,D427,R275,U739,R181,U333,L215,U808,R341,D537,R336,U230,R247,U748,R846,U404,R850,D493,R891,U176,L744,U585,L987,D849,R271,D848,L555,U801,R316,U753,L390,U97,L128,U45,R706,U35,L928,U913,R537,D512,R152,D410,R76,D209,R183,U941,R289,U632,L923,D190,R488,D934,R442,D303,R178,D250,R204,U247,R707,U77,R428,D701,R386,U110,R641,U925,R703,D387,L946,U415,R461,D123,L214,U236,L959,U517,R957,D524,R812,D668,R369,U340,L606,D503,R755,U390,R142,D921,L976,D36,L965,D450,L722,D224,L303,U705,L584
L993,U810,L931,D139,R114,D77,L75,U715,R540,D994,L866,U461,R340,D179,R314,D423,R629,D8,L692,U446,L88,D132,L128,U934,L465,D58,L696,D883,L955,D565,R424,U286,R403,U57,L627,D930,R887,D941,L306,D951,R918,U587,R939,U821,L65,D18,L987,D707,L360,D54,L932,U366,R625,U609,R173,D637,R661,U888,L68,U962,R270,U369,R780,U845,L813,U481,R66,D182,R420,U605,R880,D276,L6,D529,R883,U189,R380,D472,R30,U35,L510,D844,L146,U875,R152,U545,R274,U920,R432,U814,R583,D559,L820,U135,L353,U975,L103,U615,R401,U692,L676,D781,R551,D985,L317,U836,R115,D216,L967,U286,R681,U144,L354,U678,L893,D487,R664,D185,R787,D909,L582,D283,L519,D893,L56,U768,L345,D992,L248,U439,R573,D98,L390,D43,L470,D435,R176,U468,R688,U388,L377,U800,R187,U641,L268,U857,L716,D179,R212,U196,L342,U731,R261,D92,R183,D623,L589,D215,L966,U878,L784,U740,R823,D99,L167,D992,R414,U22,L27,U390,R286,D744,L360,U554,L756,U715,R939,D806,R279,U292,L960,U633,L428,U949,R90,D321,R749,U395,L392,U348,L33,D757,R289,D367,L562,D668,L79,D193,L991,D705,L562,U25,R146,D34,R325,U203,R403,D714,R607,U72,L444,D76,R267,U924,R289,U962,L159,U726,L57,D540,R299,U343,R936,U90,L311,U243,L415,D426,L936,D570,L539,D731,R367,D374,L56,D251,L265,U65,L14,D882,L956,U88,R688,D34,R866,U777,R342,D270,L344,D953,L438,D855,L587,U320,L953,D945,L473,U559,L487,D602,R255,U871,L854,U45,R705,D247,R955,U885,R657,D664,L360,D764,L549,D676,R85,U189,L951,D922,R511,D429,R37,U11,R821,U984,R825,U874,R753,D524,L537,U618,L919,D597,L364,D231,L258,U818,R406,D208,R214,U530,R261

View file

@ -0,0 +1,2 @@
R75,D30,R83,U83,L12,D49,R71,U7,L72
U62,R66,U55,R34,D71,R55,D58,R83

View file

@ -0,0 +1,8 @@
[package]
name = "day04_secure_container"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,39 @@
You arrive at the Venus fuel depot only to discover it's protected by a password. The Elves had written the password on a sticky note, but someone threw it out.
However, they do remember a few key facts about the password:
* It is a six-digit number.
* The value is within the range given in your puzzle input.
* Two adjacent digits are the same (like `22` in `1*22*345`).
* Going from left to right, the digits *never decrease*; they only ever increase or stay the same (like `111123` or `135679`).
Other than the range rule, the following are true:
* `111111` meets these criteria (double `11`, never decreases).
* `2234*50*` does not meet these criteria (decreasing pair of digits `50`).
* `123789` does not meet these criteria (no double).
*How many different passwords* within the range given in your puzzle input meet these criteria?
Your puzzle answer was `921`.
\--- Part Two ---
----------
An Elf just remembered one more important detail: the two adjacent matching digits *are not part of a larger group of matching digits*.
Given this additional criterion, but still ignoring the range rule, the following are now true:
* `112233` meets these criteria because the digits never decrease and all repeated digits are exactly two digits long.
* `123*444*` no longer meets the criteria (the repeated `44` is part of a larger group of `444`).
* `111122` meets the criteria (even though `1` is repeated more than twice, it still contains a double `22`).
*How many different passwords* within the range given in your puzzle input meet all of the criteria?
Your puzzle answer was `603`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
Your puzzle input was `278384-824795`.

View file

@ -0,0 +1,66 @@
pub fn run(input: &str) -> (usize, usize) {
let range: Vec<_> = input.split('-').map(|n| n.parse::<usize>().unwrap()).collect();
let valid_1: Vec<_> = (range[0]..=range[1]).filter(is_valid_1).collect();
let first = valid_1.len();
let second = valid_1.into_iter().filter(is_valid_2).count();
(first, second)
}
fn is_valid_1(password: &usize) -> bool {
let mut double_found = false;
let mut last_digit = password % 10;
let mut remaining = password / 10;
while remaining > 0 {
let this_digit = remaining % 10;
if this_digit > last_digit {
return false;
}
if this_digit == last_digit {
double_found = true;
}
last_digit = this_digit;
remaining /= 10;
}
double_found
}
fn is_valid_2(password: &usize) -> bool {
let mut current_group_len = 1;
let mut last_digit = password % 10;
let mut remaining = password / 10;
while remaining > 0 {
let this_digit = remaining % 10;
if this_digit == last_digit {
current_group_len += 1;
} else if current_group_len == 2 {
return true;
} else {
current_group_len = 1;
}
last_digit = this_digit;
remaining /= 10;
}
current_group_len == 2
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_sample() {
let sample_input = read_file("tests/sample_input");
assert_eq!(run(&sample_input), (36, 14));
}
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (921, 603));
}
}

View file

@ -0,0 +1 @@
278384-824795

View file

@ -0,0 +1 @@
111200-111300

View file

@ -0,0 +1,9 @@
[package]
name = "day05_sunny_with_a_chance_of_asteroids"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
intcode_processor = { path = "../common/intcode_processor" }

View file

@ -0,0 +1,106 @@
You're starting to sweat as the ship makes its way toward Mercury. The Elves suggest that you get the air conditioner working by upgrading your ship computer to support the Thermal Environment Supervision Terminal.
The Thermal Environment Supervision Terminal (TEST) starts by running a *diagnostic program* (your puzzle input). The TEST diagnostic program will run on [your existing Intcode computer](2) after a few modifications:
*First*, you'll need to add *two new instructions*:
* Opcode `3` takes a single integer as *input* and saves it to the position given by its only parameter. For example, the instruction `3,50` would take an input value and store it at address `50`.
* Opcode `4` *outputs* the value of its only parameter. For example, the instruction `4,50` would output the value at address `50`.
Programs that use these instructions will come with documentation that explains what should be connected to the input and output. The program `3,0,4,0,99` outputs whatever it gets as input, then halts.
*Second*, you'll need to add support for *parameter modes*:
Each parameter of an instruction is handled based on its parameter mode. Right now, your ship computer already understands parameter mode `0`, *position mode*, which causes the parameter to be interpreted as a *position* - if the parameter is `50`, its value is *the value stored at address `50` in memory*. Until now, all parameters have been in position mode.
Now, your ship computer will also need to handle parameters in mode `1`, *immediate mode*. In immediate mode, a parameter is interpreted as a *value* - if the parameter is `50`, its value is simply *`50`*.
Parameter modes are stored in the same value as the instruction's opcode. The opcode is a two-digit number based only on the ones and tens digit of the value, that is, the opcode is the rightmost two digits of the first value in an instruction. Parameter modes are single digits, one per parameter, read right-to-left from the opcode: the first parameter's mode is in the hundreds digit, the second parameter's mode is in the thousands digit, the third parameter's mode is in the ten-thousands digit, and so on. Any missing modes are `0`.
For example, consider the program `1002,4,3,4,33`.
The first instruction, `1002,4,3,4`, is a *multiply* instruction - the rightmost two digits of the first value, `02`, indicate opcode `2`, multiplication. Then, going right to left, the parameter modes are `0` (hundreds digit), `1` (thousands digit), and `0` (ten-thousands digit, not present and therefore zero):
```
ABCDE
1002
DE - two-digit opcode, 02 == opcode 2
C - mode of 1st parameter, 0 == position mode
B - mode of 2nd parameter, 1 == immediate mode
A - mode of 3rd parameter, 0 == position mode,
omitted due to being a leading zero
```
This instruction multiplies its first two parameters. The first parameter, `4` in position mode, works like it did before - its value is the value stored at address `4` (`33`). The second parameter, `3` in immediate mode, simply has value `3`. The result of this operation, `33 * 3 = 99`, is written according to the third parameter, `4` in position mode, which also works like it did before - `99` is written to address `4`.
Parameters that an instruction writes to will *never be in immediate mode*.
*Finally*, some notes:
* It is important to remember that the instruction pointer should increase by *the number of values in the instruction* after the instruction finishes. Because of the new instructions, this amount is no longer always `4`.
* Integers can be negative: `1101,100,-1,4,0` is a valid program (find `100 + -1`, store the result in position `4`).
The TEST diagnostic program will start by requesting from the user the ID of the system to test by running an *input* instruction - provide it `1`, the ID for the ship's air conditioner unit.
It will then perform a series of diagnostic tests confirming that various parts of the Intcode computer, like parameter modes, function correctly. For each test, it will run an *output* instruction indicating how far the result of the test was from the expected value, where `0` means the test was successful. Non-zero outputs mean that a function is not working correctly; check the instructions that were run before the output instruction to see which one failed.
Finally, the program will output a *diagnostic code* and immediately halt. This final output isn't an error; an output followed immediately by a halt means the program finished. If all outputs were zero except the diagnostic code, the diagnostic program ran successfully.
After providing `1` to the only input instruction and passing all the tests, *what diagnostic code does the program produce?*
Your puzzle answer was `9025675`.
\--- Part Two ---
----------
The air conditioner comes online! Its cold air feels good for a while, but then the TEST alarms start to go off. Since the air conditioner can't vent its heat anywhere but back into the spacecraft, it's actually making the air inside the ship *warmer*.
Instead, you'll need to use the TEST to extend the [thermal radiators](https://en.wikipedia.org/wiki/Spacecraft_thermal_control). Fortunately, the diagnostic program (your puzzle input) is already equipped for this. Unfortunately, your Intcode computer is not.
Your computer is only missing a few opcodes:
* Opcode `5` is *jump-if-true*: if the first parameter is *non-zero*, it sets the instruction pointer to the value from the second parameter. Otherwise, it does nothing.
* Opcode `6` is *jump-if-false*: if the first parameter *is zero*, it sets the instruction pointer to the value from the second parameter. Otherwise, it does nothing.
* Opcode `7` is *less than*: if the first parameter is *less than* the second parameter, it stores `1` in the position given by the third parameter. Otherwise, it stores `0`.
* Opcode `8` is *equals*: if the first parameter is *equal to* the second parameter, it stores `1` in the position given by the third parameter. Otherwise, it stores `0`.
Like all instructions, these instructions need to support *parameter modes* as described above.
Normally, after an instruction is finished, the instruction pointer increases by the number of values in that instruction. *However*, if the instruction modifies the instruction pointer, that value is used and the instruction pointer is *not automatically increased*.
For example, here are several programs that take one input, compare it to the value `8`, and then produce one output:
* `3,9,8,9,10,9,4,9,99,-1,8` - Using *position mode*, consider whether the input is *equal to* `8`; output `1` (if it is) or `0` (if it is not).
* `3,9,7,9,10,9,4,9,99,-1,8` - Using *position mode*, consider whether the input is *less than* `8`; output `1` (if it is) or `0` (if it is not).
* `3,3,1108,-1,8,3,4,3,99` - Using *immediate mode*, consider whether the input is *equal to* `8`; output `1` (if it is) or `0` (if it is not).
* `3,3,1107,-1,8,3,4,3,99` - Using *immediate mode*, consider whether the input is *less than* `8`; output `1` (if it is) or `0` (if it is not).
Here are some jump tests that take an input, then output `0` if the input was zero or `1` if the input was non-zero:
* `3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9` (using *position mode*)
* `3,3,1105,-1,9,1101,0,0,12,4,12,99,1` (using *immediate mode*)
Here's a larger example:
```
3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,
1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,
999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99
```
The above example program uses an input instruction to ask for a single number. The program will then output `999` if the input value is below `8`, output `1000` if the input value is equal to `8`, or output `1001` if the input value is greater than `8`.
This time, when the TEST diagnostic program runs its input instruction to get the ID of the system to test, *provide it `5`*, the ID for the ship's thermal radiator controller. This diagnostic test suite only outputs one number, the *diagnostic code*.
*What is the diagnostic code for system ID `5`?*
Your puzzle answer was `11981754`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
If you still want to see it, you can [get your puzzle input](5/input).

View file

@ -0,0 +1,41 @@
use intcode_processor::intcode_processor::{Cpu, OutputState};
pub fn run(input: &str) -> (isize, isize) {
let mut cpu = Cpu::with_memory(input.split(',').map(|s| s.parse::<isize>().unwrap()).collect());
let mut cpu_2 = cpu.clone();
cpu.set_input(1);
let first = run_diagnostics(&mut cpu);
cpu_2.set_input(5);
let second = run_diagnostics(&mut cpu_2);
(first, second)
}
fn run_diagnostics(cpu: &mut Cpu) -> isize {
loop {
match cpu.run() {
OutputState::Halt => (),
OutputState::Output(e) => eprintln!("{e}"),
OutputState::DiagnosticCode(out) => {
return out;
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (9025675, 11981754));
}
}

View file

@ -0,0 +1 @@
3,225,1,225,6,6,1100,1,238,225,104,0,1102,79,14,225,1101,17,42,225,2,74,69,224,1001,224,-5733,224,4,224,1002,223,8,223,101,4,224,224,1,223,224,223,1002,191,83,224,1001,224,-2407,224,4,224,102,8,223,223,101,2,224,224,1,223,224,223,1101,18,64,225,1102,63,22,225,1101,31,91,225,1001,65,26,224,101,-44,224,224,4,224,102,8,223,223,101,3,224,224,1,224,223,223,101,78,13,224,101,-157,224,224,4,224,1002,223,8,223,1001,224,3,224,1,224,223,223,102,87,187,224,101,-4698,224,224,4,224,102,8,223,223,1001,224,4,224,1,223,224,223,1102,79,85,224,101,-6715,224,224,4,224,1002,223,8,223,1001,224,2,224,1,224,223,223,1101,43,46,224,101,-89,224,224,4,224,1002,223,8,223,101,1,224,224,1,223,224,223,1101,54,12,225,1102,29,54,225,1,17,217,224,101,-37,224,224,4,224,102,8,223,223,1001,224,3,224,1,223,224,223,1102,20,53,225,4,223,99,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,1105,0,99999,1105,227,247,1105,1,99999,1005,227,99999,1005,0,256,1105,1,99999,1106,227,99999,1106,0,265,1105,1,99999,1006,0,99999,1006,227,274,1105,1,99999,1105,1,280,1105,1,99999,1,225,225,225,1101,294,0,0,105,1,0,1105,1,99999,1106,0,300,1105,1,99999,1,225,225,225,1101,314,0,0,106,0,0,1105,1,99999,107,226,226,224,1002,223,2,223,1006,224,329,101,1,223,223,1108,677,226,224,1002,223,2,223,1006,224,344,101,1,223,223,7,677,226,224,102,2,223,223,1006,224,359,101,1,223,223,108,226,226,224,1002,223,2,223,1005,224,374,101,1,223,223,8,226,677,224,1002,223,2,223,1006,224,389,101,1,223,223,1108,226,226,224,102,2,223,223,1006,224,404,101,1,223,223,1007,677,677,224,1002,223,2,223,1006,224,419,101,1,223,223,8,677,677,224,1002,223,2,223,1005,224,434,1001,223,1,223,1008,226,226,224,102,2,223,223,1005,224,449,1001,223,1,223,1008,226,677,224,102,2,223,223,1006,224,464,101,1,223,223,1107,677,677,224,102,2,223,223,1006,224,479,101,1,223,223,107,677,677,224,1002,223,2,223,1005,224,494,1001,223,1,223,1107,226,677,224,1002,223,2,223,1005,224,509,101,1,223,223,1108,226,677,224,102,2,223,223,1006,224,524,101,1,223,223,7,226,226,224,1002,223,2,223,1005,224,539,101,1,223,223,108,677,677,224,1002,223,2,223,1005,224,554,101,1,223,223,8,677,226,224,1002,223,2,223,1005,224,569,1001,223,1,223,1008,677,677,224,102,2,223,223,1006,224,584,101,1,223,223,107,226,677,224,102,2,223,223,1005,224,599,1001,223,1,223,7,226,677,224,102,2,223,223,1005,224,614,101,1,223,223,1007,226,226,224,1002,223,2,223,1005,224,629,101,1,223,223,1107,677,226,224,1002,223,2,223,1006,224,644,101,1,223,223,108,226,677,224,102,2,223,223,1006,224,659,101,1,223,223,1007,677,226,224,102,2,223,223,1006,224,674,101,1,223,223,4,223,99,226

View file

@ -0,0 +1,8 @@
[package]
name = "day06_universal_orbit_map"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,133 @@
You've landed at the Universal Orbit Map facility on Mercury. Because navigation in space often involves transferring between orbits, the orbit maps here are useful for finding efficient routes between, for example, you and Santa. You download a map of the local orbits (your puzzle input).
Except for the universal Center of Mass (`COM`), every object in space is in orbit around exactly one other object. An [orbit](https://en.wikipedia.org/wiki/Orbit) looks roughly like this:
```
\
\
|
|
AAA--> o o <--BBB
|
|
/
/
```
In this diagram, the object `BBB` is in orbit around `AAA`. The path that `BBB` takes around `AAA` (drawn with lines) is only partly shown. In the map data, this orbital relationship is written `AAA)BBB`, which means "`BBB` is in orbit around `AAA`".
Before you use your map data to plot a course, you need to make sure it wasn't corrupted during the download. To verify maps, the Universal Orbit Map facility uses *orbit count checksums* - the total number of *direct orbits* (like the one shown above) and *indirect orbits*.
Whenever `A` orbits `B` and `B` orbits `C`, then `A` *indirectly orbits* `C`. This chain can be any number of objects long: if `A` orbits `B`, `B` orbits `C`, and `C` orbits `D`, then `A` indirectly orbits `D`.
For example, suppose you have the following map:
```
COM)B
B)C
C)D
D)E
E)F
B)G
G)H
D)I
E)J
J)K
K)L
```
Visually, the above map of orbits looks like this:
```
G - H J - K - L
/ /
COM - B - C - D - E - F
\
I
```
In this visual representation, when two objects are connected by a line, the one on the right directly orbits the one on the left.
Here, we can count the total number of orbits as follows:
* `D` directly orbits `C` and indirectly orbits `B` and `COM`, a total of `3` orbits.
* `L` directly orbits `K` and indirectly orbits `J`, `E`, `D`, `C`, `B`, and `COM`, a total of `7` orbits.
* `COM` orbits nothing.
The total number of direct and indirect orbits in this example is `*42*`.
*What is the total number of direct and indirect orbits* in your map data?
Your puzzle answer was `417916`.
\--- Part Two ---
----------
Now, you just need to figure out how many *orbital transfers* you (`YOU`) need to take to get to Santa (`SAN`).
You start at the object `YOU` are orbiting; your destination is the object `SAN` is orbiting. An orbital transfer lets you move from any object to an object orbiting or orbited by that object.
For example, suppose you have the following map:
```
COM)B
B)C
C)D
D)E
E)F
B)G
G)H
D)I
E)J
J)K
K)L
K)YOU
I)SAN
```
Visually, the above map of orbits looks like this:
```
YOU
/
G - H J - K - L
/ /
COM - B - C - D - E - F
\
I - SAN
```
In this example, `YOU` are in orbit around `K`, and `SAN` is in orbit around `I`. To move from `K` to `I`, a minimum of `4` orbital transfers are required:
* `K` to `J`
* `J` to `E`
* `E` to `D`
* `D` to `I`
Afterward, the map of orbits looks like this:
```
G - H J - K - L
/ /
COM - B - C - D - E - F
\
I - SAN
\
YOU
```
*What is the minimum number of orbital transfers required* to move from the object `YOU` are orbiting to the object `SAN` is orbiting? (Between the objects they are orbiting - *not* between `YOU` and `SAN`.)
Your puzzle answer was `523`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
If you still want to see it, you can [get your puzzle input](6/input).

View file

@ -0,0 +1,105 @@
use std::collections::HashMap;
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Orbit {
trabant_id: usize,
center_id: usize,
}
pub fn run(input: &str) -> (usize, usize) {
let graph: Vec<_> = graph_from(input);
let first = count_direct_and_indirect_orbits(&graph);
let common_orbit = get_common_center(&graph, 1, 2);
let second = distance(&graph, common_orbit, 1) + distance(&graph, common_orbit, 2) - 2;
(first, second)
}
fn graph_from(map: &str) -> Vec<Orbit> {
let mut bodies = HashMap::from([("COM", 0), ("YOU", 1), ("SAN", 2)]);
let mut graph: Vec<Orbit> = Vec::new();
for line in map.lines() {
let (center, trabant) = line.split_once(')').unwrap();
let mut bodies_len = bodies.len();
let center_id = *bodies.entry(center).or_insert(bodies_len);
bodies_len = bodies.len();
let trabant_id = *bodies.entry(trabant).or_insert(bodies_len);
graph.push(Orbit { center_id, trabant_id } );
}
graph.sort();
graph
}
fn count_direct_and_indirect_orbits(graph: &[Orbit]) -> usize {
graph.iter().map(|o| count_upstream_bodies(graph, o.trabant_id)).sum()
}
fn count_upstream_bodies(graph: &[Orbit], trabant_id: usize) -> usize {
let trabant_idx = graph.binary_search_by_key(&trabant_id, |o| o.trabant_id).unwrap();
if graph[trabant_idx].center_id == 0 {
1
} else {
1 + count_upstream_bodies(graph, graph[trabant_idx].center_id)
}
}
fn get_common_center(graph: &[Orbit], lhs: usize, rhs: usize) -> usize {
if lhs == rhs {
return lhs;
}
if is_orbited_by(graph, lhs, rhs) {
return lhs;
} else if is_orbited_by(graph, rhs, lhs) {
return rhs;
}
let trabant_idx = graph.binary_search_by_key(&lhs, |o| o.trabant_id).unwrap();
get_common_center(graph, graph[trabant_idx].center_id, rhs)
}
fn is_orbited_by(graph: &[Orbit], inner: usize, outer: usize) -> bool {
if inner == outer {
true
} else {
let outer_idx = graph.binary_search_by_key(&outer, |o| o.trabant_id).unwrap();
if graph[outer_idx].center_id == 0 {
false
} else {
is_orbited_by(graph, inner, graph[outer_idx].center_id)
}
}
}
fn distance(graph: &[Orbit], inner: usize, outer: usize) -> usize {
if inner == outer {
0
} else {
let outer_idx = graph.binary_search_by_key(&outer, |o| o.trabant_id).unwrap();
if graph[outer_idx].center_id == 0 {
usize::MAX
} else {
distance(graph, inner, graph[outer_idx].center_id) + 1
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_sample() {
let sample_input = read_file("tests/sample_input");
assert_eq!(run(&sample_input), (54, 4));
}
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (417916, 523));
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
COM)B
B)C
C)D
D)E
E)F
B)G
G)H
D)I
E)J
J)K
K)L
K)YOU
I)SAN

View file

@ -0,0 +1,9 @@
[package]
name = "day07_amplification_circuit"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
intcode_processor = { path = "../common/intcode_processor" }

View file

@ -0,0 +1,103 @@
Based on the navigational maps, you're going to need to send more power to your ship's thrusters to reach Santa in time. To do this, you'll need to configure a series of [amplifiers](https://en.wikipedia.org/wiki/Amplifier) already installed on the ship.
There are five amplifiers connected in series; each one receives an input signal and produces an output signal. They are connected such that the first amplifier's output leads to the second amplifier's input, the second amplifier's output leads to the third amplifier's input, and so on. The first amplifier's input value is `0`, and the last amplifier's output leads to your ship's thrusters.
```
O-------O O-------O O-------O O-------O O-------O
0 ->| Amp A |->| Amp B |->| Amp C |->| Amp D |->| Amp E |-> (to thrusters)
O-------O O-------O O-------O O-------O O-------O
```
The Elves have sent you some *Amplifier Controller Software* (your puzzle input), a program that should run on your [existing Intcode computer](5). Each amplifier will need to run a copy of the program.
When a copy of the program starts running on an amplifier, it will first use an input instruction to ask the amplifier for its current *phase setting* (an integer from `0` to `4`). Each phase setting is used *exactly once*, but the Elves can't remember which amplifier needs which phase setting.
The program will then call another input instruction to get the amplifier's input signal, compute the correct output signal, and supply it back to the amplifier with an output instruction. (If the amplifier has not yet received an input signal, it waits until one arrives.)
Your job is to *find the largest output signal that can be sent to the thrusters* by trying every possible combination of phase settings on the amplifiers. Make sure that memory is not shared or reused between copies of the program.
For example, suppose you want to try the phase setting sequence `3,1,2,4,0`, which would mean setting amplifier `A` to phase setting `3`, amplifier `B` to setting `1`, `C` to `2`, `D` to `4`, and `E` to `0`. Then, you could determine the output signal that gets sent from amplifier `E` to the thrusters with the following steps:
* Start the copy of the amplifier controller software that will run on amplifier `A`. At its first input instruction, provide it the amplifier's phase setting, `3`. At its second input instruction, provide it the input signal, `0`. After some calculations, it will use an output instruction to indicate the amplifier's output signal.
* Start the software for amplifier `B`. Provide it the phase setting (`1`) and then whatever output signal was produced from amplifier `A`. It will then produce a new output signal destined for amplifier `C`.
* Start the software for amplifier `C`, provide the phase setting (`2`) and the value from amplifier `B`, then collect its output signal.
* Run amplifier `D`'s software, provide the phase setting (`4`) and input value, and collect its output signal.
* Run amplifier `E`'s software, provide the phase setting (`0`) and input value, and collect its output signal.
The final output signal from amplifier `E` would be sent to the thrusters. However, this phase setting sequence may not have been the best one; another sequence might have sent a higher signal to the thrusters.
Here are some example programs:
* Max thruster signal *`43210`* (from phase setting sequence `4,3,2,1,0`):
```
3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0
```
* Max thruster signal *`54321`* (from phase setting sequence `0,1,2,3,4`):
```
3,23,3,24,1002,24,10,24,1002,23,-1,23,101,5,23,23,1,24,23,23,4,23,99,0,0
```
* Max thruster signal *`65210`* (from phase setting sequence `1,0,4,3,2`):
```
3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33,1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0
```
Try every combination of phase settings on the amplifiers. *What is the highest signal that can be sent to the thrusters?*
Your puzzle answer was `46248`.
\--- Part Two ---
----------
It's no good - in this configuration, the amplifiers can't generate a large enough output signal to produce the thrust you'll need. The Elves quickly talk you through rewiring the amplifiers into a *feedback loop*:
```
O-------O O-------O O-------O O-------O O-------O
0 -+->| Amp A |->| Amp B |->| Amp C |->| Amp D |->| Amp E |-.
| O-------O O-------O O-------O O-------O O-------O |
| |
'--------------------------------------------------------+
|
v
(to thrusters)
```
Most of the amplifiers are connected as they were before; amplifier `A`'s output is connected to amplifier `B`'s input, and so on. *However,* the output from amplifier `E` is now connected into amplifier `A`'s input. This creates the feedback loop: the signal will be sent through the amplifiers *many times*.
In feedback loop mode, the amplifiers need *totally different phase settings*: integers from `5` to `9`, again each used exactly once. These settings will cause the Amplifier Controller Software to repeatedly take input and produce output many times before halting. Provide each amplifier its phase setting at its first input instruction; all further input/output instructions are for signals.
Don't restart the Amplifier Controller Software on any amplifier during this process. Each one should continue receiving and sending signals until it halts.
All signals sent or received in this process will be between pairs of amplifiers except the very first signal and the very last signal. To start the process, a `0` signal is sent to amplifier `A`'s input *exactly once*.
Eventually, the software on the amplifiers will halt after they have processed the final loop. When this happens, the last output signal from amplifier `E` is sent to the thrusters. Your job is to *find the largest output signal that can be sent to the thrusters* using the new phase settings and feedback loop arrangement.
Here are some example programs:
* Max thruster signal *`139629729`* (from phase setting sequence `9,8,7,6,5`):
```
3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5
```
* Max thruster signal *`18216`* (from phase setting sequence `9,7,8,5,6`):
```
3,52,1001,52,-5,52,3,53,1,52,56,54,1007,54,5,55,1005,55,26,1001,54,-5,54,1105,1,12,1,53,54,53,1008,54,0,55,1001,55,1,55,2,53,55,53,4,53,1001,56,-1,56,1005,56,6,99,0,0,0,0,10
```
Try every combination of the new phase settings on the amplifier feedback loop. *What is the highest signal that can be sent to the thrusters?*
Your puzzle answer was `54163586`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
If you still want to see it, you can [get your puzzle input](7/input).

View file

@ -0,0 +1,134 @@
use std::{sync::{mpsc, Arc}, thread, collections::VecDeque};
use intcode_processor::intcode_processor::{Cpu, OutputState};
pub fn run(input: &str) -> (isize, isize) {
let template = Cpu::try_with_memory_from_str(input).unwrap();
let mut first = 0;
for perm in get_permutations(&(0..5).collect()) {
let mut output = 0;
for input in perm.iter() {
let mut amp = template.clone();
amp.set_input(*input);
amp.set_input(output);
loop {
match amp.run() {
OutputState::DiagnosticCode(out) => {
output = out;
break;
},
OutputState::Output(e) => amp.set_input(e),
OutputState::Halt => break,
}
}
}
first = first.max(output);
}
let mut second = 0;
for perm in get_permutations(&(5..=9).collect()) {
let output = Arc::new(std::sync::Mutex::new(0));
let mut receivers = VecDeque::new();
let mut transmitters = VecDeque::new();
let last = perm.len()-1;
for _ in 0..=last {
let (t, r) = mpsc::channel::<isize>();
receivers.push_back(r);
transmitters.push_back(t);
}
transmitters[0].send(0).unwrap();
transmitters.rotate_left(1);
let mut handles = Vec::new();
for (amp_idx, phase) in perm.into_iter().enumerate() {
let tx = transmitters.pop_front().unwrap();
let rx = receivers.pop_front().unwrap();
let output = Arc::clone(&output);
let mut local_out = 0;
let mut amp = template.clone();
let handle = thread::spawn(move || {
amp.set_input(phase);
loop {
amp.set_input(rx.recv().unwrap());
match amp.run() {
OutputState::Output(out) => {
local_out = out;
tx.send(out).unwrap();
},
OutputState::Halt => {
// Swallow errors, because at this point we are halting anyway, so we
// don't care about whether or not the next amp has already
// deconstructed anymore.
let _ = tx.send(0);
break;
},
OutputState::DiagnosticCode(d) => {
local_out = d;
// Swallow errors, because at this point we are halting anyway, so we
// don't care about whether or not the next amp has already
// deconstructed anymore.
let _ = tx.send(d);
break;
},
}
}
if amp_idx == last {
*output.lock().unwrap() = local_out;
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap()
}
second = second.max(*output.lock().unwrap());
}
(first, second)
}
fn get_permutations(numbers: &Vec<isize>) -> Vec<Vec<isize>> {
if numbers.len() == 1 {
vec![numbers.to_vec()]
} else {
let mut res = Vec::new();
for (idx, n) in numbers.iter().enumerate() {
let mut rest = numbers.to_vec();
rest.remove(idx);
let rest_perms = get_permutations(&rest);
for mut p in rest_perms {
p.push(*n);
res.push(p);
}
}
res
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_sample() {
let sample_input = read_file("tests/sample_input");
// The second part of the output is not verified, but that's what my solution produces.
assert_eq!(run(&sample_input), (65210, 76543));
}
#[test]
fn test_sample_2() {
let sample_input = read_file("tests/sample_input_2");
// The first part of the output is not verified, but that's what my solution produces.
assert_eq!(run(&sample_input), (0, 18216));
}
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (46248, 54163586));
}
}

View file

@ -0,0 +1 @@
3,8,1001,8,10,8,105,1,0,0,21,34,51,64,81,102,183,264,345,426,99999,3,9,102,2,9,9,1001,9,4,9,4,9,99,3,9,101,4,9,9,102,5,9,9,1001,9,2,9,4,9,99,3,9,101,3,9,9,1002,9,5,9,4,9,99,3,9,102,3,9,9,101,3,9,9,1002,9,4,9,4,9,99,3,9,1002,9,3,9,1001,9,5,9,1002,9,5,9,101,3,9,9,4,9,99,3,9,102,2,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,99,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,1,9,9,4,9,3,9,101,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,2,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,1001,9,1,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,1,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,101,1,9,9,4,9,99

View file

@ -0,0 +1 @@
3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33,1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0

View file

@ -0,0 +1 @@
3,52,1001,52,-5,52,3,53,1,52,56,54,1007,54,5,55,1005,55,26,1001,54,-5,54,1105,1,12,1,53,54,53,1008,54,0,55,1001,55,1,55,2,53,55,53,4,53,1001,56,-1,56,1005,56,6,99,0,0,0,0,10

View file

@ -0,0 +1,8 @@
[package]
name = "day08_space_image_format"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,75 @@
The Elves' spirits are lifted when they realize you have an opportunity to reboot one of their Mars rovers, and so they are curious if you would spend a brief sojourn on Mars. You land your ship near the rover.
When you reach the rover, you discover that it's already in the process of rebooting! It's just waiting for someone to enter a [BIOS](https://en.wikipedia.org/wiki/BIOS) password. The Elf responsible for the rover takes a picture of the password (your puzzle input) and sends it to you via the Digital Sending Network.
Unfortunately, images sent via the Digital Sending Network aren't encoded with any normal encoding; instead, they're encoded in a special Space Image Format. None of the Elves seem to remember why this is the case. They send you the instructions to decode it.
Images are sent as a series of digits that each represent the color of a single pixel. The digits fill each row of the image left-to-right, then move downward to the next row, filling rows top-to-bottom until every pixel of the image is filled.
Each image actually consists of a series of identically-sized *layers* that are filled in this way. So, the first digit corresponds to the top-left pixel of the first layer, the second digit corresponds to the pixel to the right of that on the same layer, and so on until the last digit, which corresponds to the bottom-right pixel of the last layer.
For example, given an image `3` pixels wide and `2` pixels tall, the image data `123456789012` corresponds to the following image layers:
```
Layer 1: 123
456
Layer 2: 789
012
```
The image you received is *`25` pixels wide and `6` pixels tall*.
To make sure the image wasn't corrupted during transmission, the Elves would like you to find the layer that contains the *fewest `0` digits*. On that layer, what is *the number of `1` digits multiplied by the number of `2` digits?*
Your puzzle answer was `1072`.
\--- Part Two ---
----------
Now you're ready to decode the image. The image is rendered by stacking the layers and aligning the pixels with the same positions in each layer. The digits indicate the color of the corresponding pixel: `0` is black, `1` is white, and `2` is transparent.
The layers are rendered with the first layer in front and the last layer in back. So, if a given position has a transparent pixel in the first and second layers, a black pixel in the third layer, and a white pixel in the fourth layer, the final image would have a *black* pixel at that position.
For example, given an image `2` pixels wide and `2` pixels tall, the image data `0222112222120000` corresponds to the following image layers:
```
Layer 1: 02
22
Layer 2: 11
22
Layer 3: 22
12
Layer 4: 00
00
```
Then, the full image can be found by determining the top visible pixel in each position:
* The top-left pixel is *black* because the top layer is `0`.
* The top-right pixel is *white* because the top layer is `2` (transparent), but the second layer is `1`.
* The bottom-left pixel is *white* because the top two layers are `2`, but the third layer is `1`.
* The bottom-right pixel is *black* because the only visible pixel in that position is `0` (from layer 4).
So, the final image looks like this:
```
01
10
```
*What message is produced after decoding your image?*
Your puzzle answer was `YLFPJ`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
If you still want to see it, you can [get your puzzle input](8/input).

View file

@ -0,0 +1,76 @@
struct Layer {
pixels: Vec<Vec<u8>>,
}
impl Layer {
fn from(bytes: &[u8], width: usize) -> Self {
Self {
pixels: bytes.chunks(width)
.map(|c| c.iter()
.map(|b| b-b'0')
.collect())
.collect()
}
}
fn count(&self, target: u8) -> usize {
self.pixels.iter().flatten().filter(|&pix| *pix == target).count()
}
}
struct Image {
layers: Vec<Layer>,
width: usize,
height: usize,
}
impl Image {
fn from(value: &str, width: usize, height: usize) -> Self {
Self {
layers: value.as_bytes().chunks(width*height).map(|c| Layer::from(c, width)).collect(),
width,
height,
}
}
fn print(&self) -> String {
(0..self.height).map(|row| (0..self.width).map(|col| self.layers.iter().find_map(|l| match l.pixels[row][col] {
0 => Some(' '),
1 => Some('#'),
_ => None,
}).unwrap()).chain(['\n'].into_iter()).collect::<String>())
.collect()
}
}
pub fn run(input: &str) -> (usize, String) {
let image = Image::from(input, 25, 6);
let layer = image.layers.iter().min_by_key(|l|l.count(0)).unwrap();
let first = layer.count(1) * layer.count(2);
let second = image.print();
(first, second)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
let img = r#"
# ## #### ### ##
# ## # # # #
# # # ### # # #
# # # ### #
# # # # # #
# #### # # ##
"#;
assert_eq!(run(&challenge_input), (1072, img[1..].to_string()));
}
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,9 @@
[package]
name = "day09_sensor_boost"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
intcode_processor = { path = "../common/intcode_processor" }

View file

@ -0,0 +1,57 @@
You've just said goodbye to the rebooted rover and left Mars when you receive a faint distress signal coming from the asteroid belt. It must be the Ceres monitoring station!
In order to lock on to the signal, you'll need to boost your sensors. The Elves send up the latest *BOOST* program - Basic Operation Of System Test.
While BOOST (your puzzle input) is capable of boosting your sensors, for tenuous safety reasons, it refuses to do so until the computer it runs on passes some checks to demonstrate it is a *complete Intcode computer*.
[Your existing Intcode computer](5) is missing one key feature: it needs support for parameters in *relative mode*.
Parameters in mode `2`, *relative mode*, behave very similarly to parameters in *position mode*: the parameter is interpreted as a position. Like position mode, parameters in relative mode can be read from or written to.
The important difference is that relative mode parameters don't count from address `0`. Instead, they count from a value called the *relative base*. The *relative base* starts at `0`.
The address a relative mode parameter refers to is itself *plus* the current *relative base*. When the relative base is `0`, relative mode parameters and position mode parameters with the same value refer to the same address.
For example, given a relative base of `50`, a relative mode parameter of `-7` refers to memory address `50 + -7 = *43*`.
The relative base is modified with the *relative base offset* instruction:
* Opcode `9` *adjusts the relative base* by the value of its only parameter. The relative base increases (or decreases, if the value is negative) by the value of the parameter.
For example, if the relative base is `2000`, then after the instruction `109,19`, the relative base would be `2019`. If the next instruction were `204,-34`, then the value at address `1985` would be output.
Your Intcode computer will also need a few other capabilities:
* The computer's available memory should be much larger than the initial program. Memory beyond the initial program starts with the value `0` and can be read or written like any other memory. (It is invalid to try to access memory at a negative address, though.)
* The computer should have support for large numbers. Some instructions near the beginning of the BOOST program will verify this capability.
Here are some example programs that use these features:
* `109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99` takes no input and produces a [copy of itself](https://en.wikipedia.org/wiki/Quine_(computing)) as output.
* `1102,34915192,34915192,7,4,7,99,0` should output a 16-digit number.
* `104,1125899906842624,99` should output the large number in the middle.
The BOOST program will ask for a single input; run it in test mode by providing it the value `1`. It will perform a series of checks on each opcode, output any opcodes (and the associated parameter modes) that seem to be functioning incorrectly, and finally output a BOOST keycode.
Once your Intcode computer is fully functional, the BOOST program should report no malfunctioning opcodes when run in test mode; it should only output a single value, the BOOST keycode. *What BOOST keycode does it produce?*
Your puzzle answer was `2316632620`.
\--- Part Two ---
----------
*You now have a complete Intcode computer.*
Finally, you can lock on to the Ceres distress signal! You just need to boost your sensors using the BOOST program.
The program runs in sensor boost mode by providing the input instruction the value `2`. Once run, it will boost the sensors automatically, but it might take a few seconds to complete the operation on slower hardware. In sensor boost mode, the program will output a single value: *the coordinates of the distress signal*.
Run the BOOST program in sensor boost mode. *What are the coordinates of the distress signal?*
Your puzzle answer was `78869`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
If you still want to see it, you can [get your puzzle input](9/input).

Binary file not shown.

View file

@ -0,0 +1,33 @@
use intcode_processor::intcode_processor::{Cpu, OutputState};
pub fn run(input: &str) -> (isize, isize) {
let mut cpu = Cpu::try_with_memory_from_str(input).unwrap();
let mut cpu_2 = cpu.clone();
cpu.set_input(1);
let first = match cpu.run() {
OutputState::DiagnosticCode(d) => d,
e => panic!("Unexpected return code: {e:?}"),
};
cpu_2.set_input(2);
let second = match cpu_2.run() {
OutputState::DiagnosticCode(d) => d,
e => panic!("Unexpected return code: {e:?}"),
};
(first, second)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (2316632620, 78869));
}
}

View file

@ -0,0 +1 @@
1102,34463338,34463338,63,1007,63,34463338,63,1005,63,53,1101,3,0,1000,109,988,209,12,9,1000,209,6,209,3,203,0,1008,1000,1,63,1005,63,65,1008,1000,2,63,1005,63,904,1008,1000,0,63,1005,63,58,4,25,104,0,99,4,0,104,0,99,4,17,104,0,99,0,0,1102,1,37,1000,1101,856,0,1029,1101,286,0,1025,1101,39,0,1004,1101,861,0,1028,1101,845,0,1026,1102,28,1,1002,1102,1,0,1020,1101,0,892,1023,1101,0,291,1024,1101,35,0,1018,1101,0,27,1006,1102,1,26,1011,1101,33,0,1019,1102,31,1,1014,1102,1,36,1010,1102,23,1,1007,1101,0,32,1016,1101,29,0,1008,1101,20,0,1001,1102,1,25,1015,1101,38,0,1017,1101,0,24,1012,1102,1,22,1005,1101,1,0,1021,1101,0,21,1003,1102,1,838,1027,1102,1,30,1013,1101,895,0,1022,1101,0,34,1009,109,7,1208,0,22,63,1005,63,201,1001,64,1,64,1105,1,203,4,187,1002,64,2,64,109,-6,2102,1,5,63,1008,63,24,63,1005,63,223,1105,1,229,4,209,1001,64,1,64,1002,64,2,64,109,17,21102,40,1,-6,1008,1012,40,63,1005,63,255,4,235,1001,64,1,64,1106,0,255,1002,64,2,64,109,-15,21108,41,41,9,1005,1012,277,4,261,1001,64,1,64,1106,0,277,1002,64,2,64,109,11,2105,1,10,4,283,1105,1,295,1001,64,1,64,1002,64,2,64,109,-9,21101,42,0,8,1008,1013,44,63,1005,63,315,1105,1,321,4,301,1001,64,1,64,1002,64,2,64,109,13,1206,3,337,1001,64,1,64,1106,0,339,4,327,1002,64,2,64,109,-10,1208,0,29,63,1005,63,361,4,345,1001,64,1,64,1106,0,361,1002,64,2,64,109,2,2108,27,-4,63,1005,63,383,4,367,1001,64,1,64,1105,1,383,1002,64,2,64,109,-4,1207,2,30,63,1005,63,405,4,389,1001,64,1,64,1105,1,405,1002,64,2,64,109,22,1205,-8,417,1106,0,423,4,411,1001,64,1,64,1002,64,2,64,109,-27,2108,19,0,63,1005,63,443,1001,64,1,64,1106,0,445,4,429,1002,64,2,64,109,13,21108,43,45,-1,1005,1013,461,1106,0,467,4,451,1001,64,1,64,1002,64,2,64,109,1,21107,44,45,4,1005,1019,485,4,473,1105,1,489,1001,64,1,64,1002,64,2,64,109,-8,2102,1,-7,63,1008,63,37,63,1005,63,515,4,495,1001,64,1,64,1106,0,515,1002,64,2,64,109,1,2107,38,-4,63,1005,63,533,4,521,1105,1,537,1001,64,1,64,1002,64,2,64,109,4,21107,45,44,1,1005,1013,553,1106,0,559,4,543,1001,64,1,64,1002,64,2,64,109,-7,2107,21,-4,63,1005,63,575,1106,0,581,4,565,1001,64,1,64,1002,64,2,64,109,9,1205,7,599,4,587,1001,64,1,64,1105,1,599,1002,64,2,64,109,-11,2101,0,-3,63,1008,63,40,63,1005,63,619,1105,1,625,4,605,1001,64,1,64,1002,64,2,64,109,1,2101,0,-2,63,1008,63,28,63,1005,63,651,4,631,1001,64,1,64,1106,0,651,1002,64,2,64,109,1,21102,46,1,7,1008,1012,44,63,1005,63,671,1106,0,677,4,657,1001,64,1,64,1002,64,2,64,109,4,1201,-7,0,63,1008,63,28,63,1005,63,699,4,683,1105,1,703,1001,64,1,64,1002,64,2,64,109,-6,1207,-3,36,63,1005,63,719,1105,1,725,4,709,1001,64,1,64,1002,64,2,64,109,-4,1201,6,0,63,1008,63,23,63,1005,63,745,1106,0,751,4,731,1001,64,1,64,1002,64,2,64,109,8,1202,-6,1,63,1008,63,20,63,1005,63,777,4,757,1001,64,1,64,1105,1,777,1002,64,2,64,109,5,1202,-5,1,63,1008,63,25,63,1005,63,801,1001,64,1,64,1105,1,803,4,783,1002,64,2,64,109,8,21101,47,0,-6,1008,1014,47,63,1005,63,829,4,809,1001,64,1,64,1106,0,829,1002,64,2,64,109,1,2106,0,6,1001,64,1,64,1106,0,847,4,835,1002,64,2,64,109,11,2106,0,-4,4,853,1105,1,865,1001,64,1,64,1002,64,2,64,109,-15,1206,3,883,4,871,1001,64,1,64,1106,0,883,1002,64,2,64,109,14,2105,1,-8,1105,1,901,4,889,1001,64,1,64,4,64,99,21102,1,27,1,21102,1,915,0,1106,0,922,21201,1,57564,1,204,1,99,109,3,1207,-2,3,63,1005,63,964,21201,-2,-1,1,21102,1,942,0,1105,1,922,22101,0,1,-1,21201,-2,-3,1,21101,957,0,0,1105,1,922,22201,1,-1,-2,1106,0,968,21202,-2,1,-2,109,-3,2106,0,0

View file

@ -0,0 +1,8 @@
[package]
name = "day10_monitoring_station"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,215 @@
You fly into the asteroid belt and reach the Ceres monitoring station. The Elves here have an emergency: they're having trouble tracking all of the asteroids and can't be sure they're safe.
The Elves would like to build a new monitoring station in a nearby area of space; they hand you a map of all of the asteroids in that region (your puzzle input).
The map indicates whether each position is empty (`.`) or contains an asteroid (`#`). The asteroids are much smaller than they appear on the map, and every asteroid is exactly in the center of its marked position. The asteroids can be described with `X,Y` coordinates where `X` is the distance from the left edge and `Y` is the distance from the top edge (so the top-left corner is `0,0` and the position immediately to its right is `1,0`).
Your job is to figure out which asteroid would be the best place to build a *new monitoring station*. A monitoring station can *detect* any asteroid to which it has *direct line of sight* - that is, there cannot be another asteroid *exactly* between them. This line of sight can be at any angle, not just lines aligned to the grid or diagonally. The *best* location is the asteroid that can *detect* the largest number of other asteroids.
For example, consider the following map:
```
.#..#
.....
#####
....#
...##
```
The best location for a new monitoring station on this map is the highlighted asteroid at `3,4` because it can detect `8` asteroids, more than any other location. (The only asteroid it cannot detect is the one at `1,0`; its view of this asteroid is blocked by the asteroid at `2,2`.) All other asteroids are worse locations; they can detect `7` or fewer other asteroids. Here is the number of other asteroids a monitoring station on each asteroid could detect:
```
.7..7
.....
67775
....7
...87
```
Here is an asteroid (`#`) and some examples of the ways its line of sight might be blocked. If there were another asteroid at the location of a capital letter, the locations marked with the corresponding lowercase letter would be blocked and could not be detected:
```
#.........
...A......
...B..a...
.EDCG....a
..F.c.b...
.....c....
..efd.c.gb
.......c..
....f...c.
...e..d..c
```
Here are some larger examples:
* Best is `5,8` with `33` other asteroids detected:
```
......#.#.
#..#.#....
..#######.
.#.#.###..
.#..#.....
..#....#.#
#..#....#.
.##.#..###
##...#..#.
.#....####
```
* Best is `1,2` with `35` other asteroids detected:
```
#.#...#.#.
.###....#.
.#....#...
##.#.#.#.#
....#.#.#.
.##..###.#
..#...##..
..##....##
......#...
.####.###.
```
* Best is `6,3` with `41` other asteroids detected:
```
.#..#..###
####.###.#
....###.#.
..###.##.#
##.##.#.#.
....###..#
..#.#..#.#
#..#.#.###
.##...##.#
.....#.#..
```
* Best is `11,13` with `210` other asteroids detected:
```
.#..##.###...#######
##.############..##.
.#.######.########.#
.###.#######.####.#.
#####.##.#.##.###.##
..#####..#.#########
####################
#.####....###.#.#.##
##.#################
#####.##.###..####..
..######..##.#######
####.##.####...##..#
.#####..#.######.###
##...#.##########...
#.##########.#######
.####.#.###.###.#.##
....##.##.###..#####
.#.#.###########.###
#.#.#.#####.####.###
###.##.####.##.#..##
```
Find the best location for a new monitoring station. *How many other asteroids can be detected from that location?*
Your puzzle answer was `263`.
\--- Part Two ---
----------
Once you give them the coordinates, the Elves quickly deploy an Instant Monitoring Station to the location and discover the worst: there are simply too many asteroids.
The only solution is *complete vaporization by giant laser*.
Fortunately, in addition to an asteroid scanner, the new monitoring station also comes equipped with a giant rotating laser perfect for vaporizing asteroids. The laser starts by pointing *up* and always rotates *clockwise*, vaporizing any asteroid it hits.
If multiple asteroids are *exactly* in line with the station, the laser only has enough power to vaporize *one* of them before continuing its rotation. In other words, the same asteroids that can be *detected* can be vaporized, but if vaporizing one asteroid makes another one detectable, the newly-detected asteroid won't be vaporized until the laser has returned to the same position by rotating a full 360 degrees.
For example, consider the following map, where the asteroid with the new monitoring station (and laser) is marked `X`:
```
.#....#####...#..
##...##.#####..##
##...#...#.#####.
..#.....X...###..
..#.#.....#....##
```
The first nine asteroids to get vaporized, in order, would be:
```
.#....###24...#..
##...##.13#67..9#
##...#...5.8####.
..#.....X...###..
..#.#.....#....##
```
Note that some asteroids (the ones behind the asteroids marked `1`, `5`, and `7`) won't have a chance to be vaporized until the next full rotation. The laser continues rotating; the next nine to be vaporized are:
```
.#....###.....#..
##...##...#.....#
##...#......1234.
..#.....X...5##..
..#.9.....8....76
```
The next nine to be vaporized are then:
```
.8....###.....#..
56...9#...#.....#
34...7...........
..2.....X....##..
..1..............
```
Finally, the laser completes its first full rotation (`1` through `3`), a second rotation (`4` through `8`), and vaporizes the last asteroid (`9`) partway through its third rotation:
```
......234.....6..
......1...5.....7
.................
........X....89..
.................
```
In the large example above (the one with the best monitoring station location at `11,13`):
* The 1st asteroid to be vaporized is at `11,12`.
* The 2nd asteroid to be vaporized is at `12,1`.
* The 3rd asteroid to be vaporized is at `12,2`.
* The 10th asteroid to be vaporized is at `12,8`.
* The 20th asteroid to be vaporized is at `16,0`.
* The 50th asteroid to be vaporized is at `16,9`.
* The 100th asteroid to be vaporized is at `10,16`.
* The 199th asteroid to be vaporized is at `9,6`.
* *The 200th asteroid to be vaporized is at `8,2`.*
* The 201st asteroid to be vaporized is at `10,9`.
* The 299th and final asteroid to be vaporized is at `11,1`.
The Elves are placing bets on which will be the *200th* asteroid to be vaporized. Win the bet by determining which asteroid that will be; *what do you get if you multiply its X coordinate by `100` and then add its Y coordinate?* (For example, `8,2` becomes *`802`*.)
Your puzzle answer was `1110`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
If you still want to see it, you can [get your puzzle input](10/input).

View file

@ -0,0 +1,138 @@
use std::cmp::Ordering;
use std::collections::BTreeSet;
#[derive(PartialEq, Eq)]
struct RationalAngle {
upper_half: bool,
divident: isize,
divisor: usize,
}
impl From<(isize, isize)> for RationalAngle {
fn from((x, y): (isize, isize)) -> Self {
let upper_half = x < 0;
let divident = y.signum() * x / gcd(x, y);
let divisor = (y / gcd(x, y)).unsigned_abs();
Self {
upper_half,
divident,
divisor,
}
}
}
impl PartialOrd for RationalAngle {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self.upper_half, other.upper_half) {
(true, false) => Some(Ordering::Greater),
(false, true) => Some(Ordering::Less),
_ => match (self. divident as f64 / self.divisor as f64) - (other.divident as f64 / other.divisor as f64) {
n if n < 0.0 => Some(Ordering::Greater),
p if p > 0.0 => Some(Ordering::Less),
_ => Some(Ordering::Equal),
}
}
}
}
impl Ord for RationalAngle {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
impl RationalAngle {
fn from_vector(source: (usize, usize), dest: (usize, usize)) -> Self {
let x = dest.0 as isize - source.0 as isize;
let y = dest.1 as isize - source.1 as isize;
Self::from((x, y))
}
}
pub fn run(input: &str) -> (usize, usize) {
let asteroids: Vec<(usize, usize)> = input.lines().enumerate().flat_map(|(y, line)| line.chars().enumerate().filter(|(_x, c)| *c == '#').map(|(x, _c)| (x, y)).collect::<Vec<_>>()).collect();
// let mut angles: Vec<Vec<(isize, isize)>> = asteroids.iter().map(|a| asteroids.iter().filter(|other| *other != a).map(|other| reduced_angle(*a, *other)).collect()).collect();
let mut angles: Vec<BTreeSet<RationalAngle>> = asteroids.iter().map(|a| asteroids.iter().filter(|other| *other != a).map(|other| RationalAngle::from_vector(*a, *other)).collect()).collect();
// for asteroid in angles.iter_mut() {
// asteroid.sort_by(rotation_sort);
// asteroid.dedup();
// }
let (idx, first) = angles.iter().enumerate().max_by_key(|(_idx, a)| a.len()).unwrap();
let angle = first.iter().nth(199).unwrap();
let second = (0, 0);
// let second = (1..).map(|i| ((asteroids[idx].0 as isize + i * angle.divident) as usize, (asteroids[idx].1 as isize + i * angle.divisor as isize) as usize))
// .find(|a| asteroids.contains(a))
// .unwrap();
(first.len(), (second.0*100 + second.1))
}
fn rotation_sort(lhs: &(isize, isize), rhs: &(isize, isize)) -> Ordering {
if lhs == rhs {
return Ordering::Equal;
}
match (lhs.0.signum(), rhs.0.signum()) {
(-1, 1) => Ordering::Greater,
(1, -1) => Ordering::Less,
(0, -1) =>Ordering::Less,
(-1, 0) => Ordering::Greater,
(0, 1) => if lhs.1.signum() == 1 { Ordering::Less } else { Ordering::Greater },
(1, 0) => if rhs.1.signum() == 1 { Ordering::Greater } else { Ordering::Less },
(0, 0) => match (lhs.1.signum(), rhs.1.signum()) {
(1, 1) | (-1, -1) => Ordering::Equal,
(1, -1) => Ordering::Less,
(-1, 1) => Ordering::Greater,
_ => panic!("Unable to sort {lhs:?} and {rhs:?}"),
},
_ => match (lhs.1.signum(), rhs.1.signum()) {
(1, 1) | (-1, -1) => match (lhs.0 as f64 / lhs.1 as f64) - (rhs.0 as f64 / rhs.1 as f64) {
n if n < 0.0 => Ordering::Greater,
p if p > 0.0 => Ordering::Less,
_ => panic!("Unexpected sorting of {lhs:?} and {rhs:?}: Equal"),
},
_ => (lhs.0.signum()*lhs.1.signum()).cmp(&(lhs.0.signum()*rhs.1.signum()))
},
}
}
fn gcd(lhs: isize, rhs: isize) -> isize {
if lhs == 0 {
return rhs.abs();
} else if rhs == 0 {
return lhs.abs();
}
let a = lhs.abs();
let b = rhs.abs();
(1..=a.min(b)).rev().find(|i| a%i == 0 && b%i == 0).unwrap()
}
fn reduced_angle(lhs: (usize, usize), rhs: (usize, usize)) -> (isize, isize) {
let x = rhs.0 as isize - lhs.0 as isize;
let y = rhs.1 as isize - lhs.1 as isize;
(x/gcd(x,y), y/gcd(x,y))
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
// #[test]
// fn test_sample() {
// let sample_input = read_file("tests/sample_input");
// assert_eq!(run(&sample_input), (33, 0));
// }
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (263, 1110));
}
}

View file

@ -0,0 +1,33 @@
.#..#..##.#...###.#............#.
.....#..........##..#..#####.#..#
#....#...#..#.......#...........#
.#....#....#....#.#...#.#.#.#....
..#..#.....#.......###.#.#.##....
...#.##.###..#....#........#..#.#
..#.##..#.#.#...##..........#...#
..#..#.......................#..#
...#..#.#...##.#...#.#..#.#......
......#......#.....#.............
.###..#.#..#...#..#.#.......##..#
.#...#.................###......#
#.#.......#..####.#..##.###.....#
.#.#..#.#...##.#.#..#..##.#.#.#..
##...#....#...#....##....#.#....#
......#..#......#.#.....##..#.#..
##.###.....#.#.###.#..#..#..###..
#...........#.#..#..#..#....#....
..........#.#.#..#.###...#.....#.
...#.###........##..#..##........
.###.....#.#.###...##.........#..
#.#...##.....#.#.........#..#.###
..##..##........#........#......#
..####......#...#..........#.#...
......##...##.#........#...##.##.
.#..###...#.......#........#....#
...##...#..#...#..#..#.#.#...#...
....#......#.#............##.....
#......####...#.....#...#......#.
...#............#...#..#.#.#..#.#
.#...#....###.####....#.#........
#.#...##...#.##...#....#.#..##.#.
.#....#.###..#..##.#.##...#.#..##

View file

@ -0,0 +1,10 @@
......#.#.
#..#.#....
..#######.
.#.#.###..
.#..#.....
..#....#.#
#..#....#.
.##.#..###
##...#..#.
.#....####