mirror of
https://github.com/wezm/advent-of-code.git
synced 2024-12-18 18:29:55 +00:00
Day 7 part 2
This commit is contained in:
parent
5965959a05
commit
92d9e9fa10
3 changed files with 199 additions and 43 deletions
|
@ -5,11 +5,11 @@ fn main() -> io::Result<()> {
|
||||||
let input = fs::read_to_string("input/day5.txt")?;
|
let input = fs::read_to_string("input/day5.txt")?;
|
||||||
let data = input::read_separated_line(',', &input)?;
|
let data = input::read_separated_line(',', &input)?;
|
||||||
|
|
||||||
let mut computer = computer::Computer::new(data.clone(), vec![1], vec![]);
|
let mut computer = computer::Computer::new('1', data.clone(), vec![1], vec![]);
|
||||||
computer.run(None, None);
|
computer.run(None, None);
|
||||||
println!("Part 1: {}", computer.output());
|
println!("Part 1: {}", computer.output());
|
||||||
|
|
||||||
let mut computer = computer::Computer::new(data.clone(), vec![5], vec![]);
|
let mut computer = computer::Computer::new('2', data.clone(), vec![5], vec![]);
|
||||||
computer.run(None, None);
|
computer.run(None, None);
|
||||||
println!("Part 2: {}", computer.output());
|
println!("Part 2: {}", computer.output());
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
|
|
||||||
use advent_of_code::{computer, input};
|
use advent_of_code::computer::{ComputeResult, Computer, Output, Pipe};
|
||||||
|
use advent_of_code::input;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
const AMPLIFIERS: usize = 5;
|
const AMPLIFIERS: usize = 5;
|
||||||
|
|
||||||
|
@ -9,7 +13,7 @@ fn main() -> io::Result<()> {
|
||||||
let data = input::read_separated_line(',', &source)?;
|
let data = input::read_separated_line(',', &source)?;
|
||||||
|
|
||||||
part1(data.clone());
|
part1(data.clone());
|
||||||
// part2(data);
|
part2(data);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -20,12 +24,14 @@ fn part1(data: Vec<i32>) {
|
||||||
elements[i as usize] = i;
|
elements[i as usize] = i;
|
||||||
}
|
}
|
||||||
let max = phase_settings(elements)
|
let max = phase_settings(elements)
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|settings| {
|
.map(|settings| {
|
||||||
let mut output = 0;
|
let mut output = 0;
|
||||||
|
let mut name = 'A';
|
||||||
for phase_setting in settings.iter() {
|
for phase_setting in settings.iter() {
|
||||||
let input = vec![output, *phase_setting];
|
let input = vec![output, *phase_setting];
|
||||||
let mut computer = computer::Computer::new(data.clone(), input, vec![]);
|
let mut computer = Computer::new(name, data.clone(), input, vec![]);
|
||||||
|
name = char::try_from(name as u32 + 1).unwrap();
|
||||||
computer.run(None, None);
|
computer.run(None, None);
|
||||||
output = computer.output();
|
output = computer.output();
|
||||||
}
|
}
|
||||||
|
@ -42,7 +48,68 @@ fn part2(data: Vec<i32>) {
|
||||||
elements[i as usize - 5] = i;
|
elements[i as usize - 5] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// println!("Part 2: {}", max);
|
let max = phase_settings(elements)
|
||||||
|
.into_iter()
|
||||||
|
.map(|settings| run_part2_with_settings(data.clone(), settings))
|
||||||
|
.max()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("Part 2: {}", max);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_part2_with_settings(data: Vec<i32>, settings: [i32; AMPLIFIERS]) -> i32 {
|
||||||
|
// Construct the amplifiers and the pipes between them
|
||||||
|
// The first amplifier gets io_pipe as input
|
||||||
|
// The last amplifier gets io_pipe as output
|
||||||
|
// otherwise the get a pipe between them
|
||||||
|
let io_pipe = Rc::new(RefCell::new(Pipe::new(vec![0].into_iter().collect())));
|
||||||
|
let mut last_pipe = Rc::clone(&io_pipe);
|
||||||
|
let mut amplifiers = Vec::with_capacity(AMPLIFIERS);
|
||||||
|
for i in 0..AMPLIFIERS {
|
||||||
|
let next_pipe = if i == AMPLIFIERS - 1 {
|
||||||
|
// This is the last amplifier, its output goes to the input of the first
|
||||||
|
Rc::clone(&io_pipe)
|
||||||
|
} else {
|
||||||
|
Rc::new(RefCell::new(Pipe::new(vec![].into_iter().collect())))
|
||||||
|
};
|
||||||
|
// Add the phase setting (it's the first thing that's read)
|
||||||
|
last_pipe.borrow_mut().push_front(settings[i]);
|
||||||
|
let amp = Computer::new(
|
||||||
|
char::try_from('A' as u32 + i as u32).unwrap(),
|
||||||
|
data.clone(),
|
||||||
|
last_pipe,
|
||||||
|
Rc::clone(&next_pipe),
|
||||||
|
);
|
||||||
|
amplifiers.push(amp);
|
||||||
|
last_pipe = next_pipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now run them
|
||||||
|
let mut i = 0;
|
||||||
|
let mut halted = Vec::with_capacity(AMPLIFIERS);
|
||||||
|
loop {
|
||||||
|
match amplifiers[i].run(None, None) {
|
||||||
|
ComputeResult::Halted => {
|
||||||
|
let amp = amplifiers.remove(i);
|
||||||
|
println!("{} halted", amp.name());
|
||||||
|
halted.push(amp);
|
||||||
|
if amplifiers.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Don't increment i as we removed an element
|
||||||
|
i = i % amplifiers.len();
|
||||||
|
}
|
||||||
|
ComputeResult::NeedsInput => {
|
||||||
|
println!("{} needs input", amplifiers[i].name());
|
||||||
|
// continue to next one
|
||||||
|
i = (i + 1) % amplifiers.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need the last output sent from the last amplifier
|
||||||
|
dbg!(io_pipe.borrow());
|
||||||
|
io_pipe.last_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn phase_settings(mut elements: [i32; AMPLIFIERS]) -> Vec<[i32; AMPLIFIERS]> {
|
fn phase_settings(mut elements: [i32; AMPLIFIERS]) -> Vec<[i32; AMPLIFIERS]> {
|
||||||
|
@ -73,7 +140,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_phase_setting_generation() {
|
fn test_phase_setting_generation() {
|
||||||
let settings = phase_settings();
|
let settings = phase_settings([0, 1, 2, 3, 4]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&settings[0..6],
|
&settings[0..6],
|
||||||
&[
|
&[
|
||||||
|
@ -86,4 +153,31 @@ mod tests {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part2_example1() {
|
||||||
|
let input = vec![
|
||||||
|
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,
|
||||||
|
];
|
||||||
|
assert_eq!(run_part2_with_settings(input, [9, 8, 7, 6, 5]), 139629729);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part2_example2() {
|
||||||
|
let input = vec![
|
||||||
|
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,
|
||||||
|
];
|
||||||
|
assert_eq!(run_part2_with_settings(input, [9, 7, 8, 5, 6]), 18216);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part2_with_part1_example() {
|
||||||
|
let input = vec![
|
||||||
|
3, 15, 3, 16, 1002, 16, 10, 16, 1, 16, 15, 15, 4, 15, 99, 0, 0,
|
||||||
|
];
|
||||||
|
assert_eq!(run_part2_with_settings(input, [4, 3, 2, 1, 0]), 43210);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
type Address = i32;
|
type Address = i32;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
@ -19,7 +23,20 @@ enum Mode {
|
||||||
Address,
|
Address,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Pipe {
|
||||||
|
queue: VecDeque<i32>,
|
||||||
|
last: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ComputeResult {
|
||||||
|
Halted,
|
||||||
|
NeedsInput,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Computer<I: Input, O: Output> {
|
pub struct Computer<I: Input, O: Output> {
|
||||||
|
name: char,
|
||||||
|
ip: i32,
|
||||||
memory: Vec<i32>,
|
memory: Vec<i32>,
|
||||||
input: I,
|
input: I,
|
||||||
output: O,
|
output: O,
|
||||||
|
@ -51,6 +68,24 @@ impl Output for Vec<i32> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Input for Rc<RefCell<Pipe>> {
|
||||||
|
fn read(&mut self) -> Option<i32> {
|
||||||
|
dbg!(self.borrow_mut().queue.pop_front())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Output for Rc<RefCell<Pipe>> {
|
||||||
|
fn write(&mut self, value: i32) {
|
||||||
|
let mut pipe = self.borrow_mut();
|
||||||
|
pipe.last = Some(value);
|
||||||
|
pipe.queue.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last_value(&self) -> i32 {
|
||||||
|
self.borrow().last.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn decode(mut instruction: i32) -> Instruction {
|
fn decode(mut instruction: i32) -> Instruction {
|
||||||
let opcode = divmod(&mut instruction, 100);
|
let opcode = divmod(&mut instruction, 100);
|
||||||
|
|
||||||
|
@ -90,15 +125,25 @@ fn decode(mut instruction: i32) -> Instruction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, O> Computer<I, O> where I: Input, O: Output {
|
impl<I, O> Computer<I, O>
|
||||||
pub fn new(memory: Vec<i32>, input: I, output: O) -> Self {
|
where
|
||||||
|
I: Input,
|
||||||
|
O: Output,
|
||||||
|
{
|
||||||
|
pub fn new(name: char, memory: Vec<i32>, input: I, output: O) -> Self {
|
||||||
Computer {
|
Computer {
|
||||||
|
name,
|
||||||
|
ip: 0,
|
||||||
memory,
|
memory,
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> char {
|
||||||
|
self.name
|
||||||
|
}
|
||||||
|
|
||||||
fn read(&self, value: i32, mode: Mode) -> i32 {
|
fn read(&self, value: i32, mode: Mode) -> i32 {
|
||||||
match mode {
|
match mode {
|
||||||
Mode::Immediate => self.memory[value as usize],
|
Mode::Immediate => self.memory[value as usize],
|
||||||
|
@ -110,67 +155,74 @@ impl<I, O> Computer<I, O> where I: Input, O: Output {
|
||||||
self.memory[address as usize] = value;
|
self.memory[address as usize] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, noun: Option<i32>, verb: Option<i32>) {
|
pub fn run(&mut self, noun: Option<i32>, verb: Option<i32>) -> ComputeResult {
|
||||||
|
println!("{}: resume ip = {}", self.name, self.ip);
|
||||||
if let Some(noun) = noun {
|
if let Some(noun) = noun {
|
||||||
self.write(1, noun);
|
self.write(1, noun);
|
||||||
}
|
}
|
||||||
if let Some(verb) = verb {
|
if let Some(verb) = verb {
|
||||||
self.write(2, verb);
|
self.write(2, verb);
|
||||||
}
|
}
|
||||||
let mut ip = 0; // instruction pointer
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match decode(self.read(ip, Mode::Immediate)) {
|
match decode(self.read(self.ip, Mode::Immediate)) {
|
||||||
Instruction::Add(mode1, mode2) => {
|
Instruction::Add(mode1, mode2) => {
|
||||||
let result = self.read(ip + 1, mode1) + self.read(ip + 2, mode2);
|
let result = self.read(self.ip + 1, mode1) + self.read(self.ip + 2, mode2);
|
||||||
self.write(self.read(ip + 3, Mode::Immediate), result);
|
self.write(self.read(self.ip + 3, Mode::Immediate), result);
|
||||||
ip += 4;
|
self.ip += 4;
|
||||||
}
|
}
|
||||||
Instruction::Multiply(mode1, mode2) => {
|
Instruction::Multiply(mode1, mode2) => {
|
||||||
let result = self.read(ip + 1, mode1) * self.read(ip + 2, mode2);
|
let result = self.read(self.ip + 1, mode1) * self.read(self.ip + 2, mode2);
|
||||||
self.write(self.read(ip + 3, Mode::Immediate), result);
|
self.write(self.read(self.ip + 3, Mode::Immediate), result);
|
||||||
ip += 4;
|
self.ip += 4;
|
||||||
}
|
|
||||||
Instruction::Input => {
|
|
||||||
let value = self.input.read().expect("no more input");
|
|
||||||
self.write(self.read(ip + 1, Mode::Immediate), value);
|
|
||||||
ip += 2;
|
|
||||||
}
|
}
|
||||||
|
Instruction::Input => match self.input.read() {
|
||||||
|
Some(value) => {
|
||||||
|
self.write(self.read(self.ip + 1, Mode::Immediate), value);
|
||||||
|
self.ip += 2;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// println!("{}: pause ip = {}", self.name, self.ip);
|
||||||
|
return ComputeResult::NeedsInput;
|
||||||
|
}
|
||||||
|
},
|
||||||
Instruction::Output(mode) => {
|
Instruction::Output(mode) => {
|
||||||
self.output.write(self.read(ip + 1, mode));
|
let value = self.read(self.ip + 1, mode);
|
||||||
ip += 2;
|
println!("{}: output {}", self.name, value);
|
||||||
|
self.output.write(value);
|
||||||
|
self.ip += 2;
|
||||||
}
|
}
|
||||||
Instruction::JumpIfTrue(mode1, mode2) => {
|
Instruction::JumpIfTrue(mode1, mode2) => {
|
||||||
if self.read(ip + 1, mode1) != 0 {
|
if self.read(self.ip + 1, mode1) != 0 {
|
||||||
ip = self.read(ip + 2, mode2);
|
self.ip = self.read(self.ip + 2, mode2);
|
||||||
} else {
|
} else {
|
||||||
ip += 3;
|
self.ip += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instruction::JumpIfFalse(mode1, mode2) => {
|
Instruction::JumpIfFalse(mode1, mode2) => {
|
||||||
if self.read(ip + 1, mode1) == 0 {
|
if self.read(self.ip + 1, mode1) == 0 {
|
||||||
ip = self.read(ip + 2, mode2);
|
self.ip = self.read(self.ip + 2, mode2);
|
||||||
} else {
|
} else {
|
||||||
ip += 3;
|
self.ip += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instruction::LessThan(mode1, mode2) => {
|
Instruction::LessThan(mode1, mode2) => {
|
||||||
if self.read(ip + 1, mode1) < self.read(ip + 2, mode2) {
|
if self.read(self.ip + 1, mode1) < self.read(self.ip + 2, mode2) {
|
||||||
self.write(self.read(ip + 3, Mode::Immediate), 1);
|
self.write(self.read(self.ip + 3, Mode::Immediate), 1);
|
||||||
} else {
|
} else {
|
||||||
self.write(self.read(ip + 3, Mode::Immediate), 0);
|
self.write(self.read(self.ip + 3, Mode::Immediate), 0);
|
||||||
}
|
}
|
||||||
ip += 4;
|
self.ip += 4;
|
||||||
}
|
}
|
||||||
Instruction::Equals(mode1, mode2) => {
|
Instruction::Equals(mode1, mode2) => {
|
||||||
if self.read(ip + 1, mode1) == self.read(ip + 2, mode2) {
|
if self.read(self.ip + 1, mode1) == self.read(self.ip + 2, mode2) {
|
||||||
self.write(self.read(ip + 3, Mode::Immediate), 1);
|
self.write(self.read(self.ip + 3, Mode::Immediate), 1);
|
||||||
} else {
|
} else {
|
||||||
self.write(self.read(ip + 3, Mode::Immediate), 0);
|
self.write(self.read(self.ip + 3, Mode::Immediate), 0);
|
||||||
}
|
}
|
||||||
ip += 4;
|
self.ip += 4;
|
||||||
}
|
}
|
||||||
Instruction::Halt => break,
|
Instruction::Halt => return ComputeResult::Halted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,6 +232,16 @@ impl<I, O> Computer<I, O> where I: Input, O: Output {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Pipe {
|
||||||
|
pub fn new(queue: VecDeque<i32>) -> Self {
|
||||||
|
Pipe { queue, last: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_front(&mut self, value: i32) {
|
||||||
|
self.queue.push_front(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn divmod(value: &mut i32, divisor: i32) -> i32 {
|
fn divmod(value: &mut i32, divisor: i32) -> i32 {
|
||||||
let res = *value % divisor;
|
let res = *value % divisor;
|
||||||
*value /= divisor;
|
*value /= divisor;
|
||||||
|
@ -213,8 +275,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_day2() {
|
fn test_day2() {
|
||||||
let input = fs::read_to_string("input/day2.txt").unwrap();
|
let input = fs::read_to_string("input/day2.txt").unwrap();
|
||||||
let mut data = input::read_separated_line(',', &input).unwrap();
|
let data = input::read_separated_line(',', &input).unwrap();
|
||||||
let mut program = Computer::new(&mut data, vec![]);
|
let mut program = Computer::new('2', data, vec![], vec![]);
|
||||||
|
|
||||||
// Check that day2 still works wirh run through this implementation
|
// Check that day2 still works wirh run through this implementation
|
||||||
program.run(Some(12), Some(2));
|
program.run(Some(12), Some(2));
|
||||||
|
|
Loading…
Reference in a new issue