@ -1,4 +1,11 @@ | |||
[workspace] | |||
members = [ | |||
"day1", | |||
"day2", | |||
"day3", | |||
"day4", | |||
"day5", | |||
"day6", | |||
"day7", | |||
"shared", | |||
] |
@ -0,0 +1,10 @@ | |||
[package] | |||
name = "day2" | |||
version = "0.1.0" | |||
authors = ["Stefan Bühler <stbuehler@web.de>"] | |||
edition = "2018" | |||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
[dependencies] | |||
shared = { path = "../shared" } |
@ -0,0 +1 @@ | |||
1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,9,1,19,1,19,5,23,1,9,23,27,2,27,6,31,1,5,31,35,2,9,35,39,2,6,39,43,2,43,13,47,2,13,47,51,1,10,51,55,1,9,55,59,1,6,59,63,2,63,9,67,1,67,6,71,1,71,13,75,1,6,75,79,1,9,79,83,2,9,83,87,1,87,6,91,1,91,13,95,2,6,95,99,1,10,99,103,2,103,9,107,1,6,107,111,1,10,111,115,2,6,115,119,1,5,119,123,1,123,13,127,1,127,5,131,1,6,131,135,2,135,13,139,1,139,2,143,1,143,10,0,99,2,0,14,0 |
@ -0,0 +1,60 @@ | |||
use shared::intcode::{IntCode, CELL}; | |||
fn run(mut ic: IntCode, first: CELL, second: CELL) -> CELL { | |||
ic.data[1] = first; | |||
ic.data[2] = second; | |||
ic.simulate(); | |||
ic.data[0] | |||
} | |||
fn main() { | |||
let ic = include_str!("input.txt").parse::<IntCode>().unwrap(); | |||
println!("Run 1: {}", run(ic.clone(), 12, 2)); | |||
for limit in 0.. { | |||
for fst in 0..limit { | |||
let result = run(ic.clone(), fst, limit); | |||
if result == 19690720 { | |||
println!("Found parameters: {}", 100*fst + limit); | |||
return; | |||
} | |||
} | |||
if limit > 0 { | |||
for snd in 0..limit-1 { | |||
let result = run(ic.clone(), limit, snd); | |||
if result == 19690720 { | |||
println!("Found parameters: {}", 100*limit + snd); | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
#[cfg(test)] | |||
mod day2test { | |||
use super::*; | |||
fn parse(input: &str) -> IntCode { | |||
input.parse().unwrap() | |||
} | |||
fn run(input: &str) -> IntCode { | |||
let mut ic = parse(input); | |||
ic.simulate_step().expect_finished(); | |||
ic | |||
} | |||
#[test] | |||
fn examples1() { | |||
assert_eq!(run("1,9,10,3,2,3,11,0,99,30,40,50").data, parse("3500,9,10,70,2,3,11,0,99,30,40,50").data); | |||
assert_eq!(run("1,0,0,0,99").data, parse("2,0,0,0,99").data); | |||
assert_eq!(run("2,3,0,3,99").data, parse("2,3,0,6,99").data); | |||
assert_eq!(run("2,4,4,5,99,0").data, parse("2,4,4,5,99,9801").data); | |||
assert_eq!(run("1,1,1,4,99,5,6,0,99").data, parse("30,1,1,4,2,5,6,0,99").data); | |||
} | |||
#[test] | |||
fn examples2() { | |||
// no examples present | |||
} | |||
} |
@ -0,0 +1,9 @@ | |||
[package] | |||
name = "day3" | |||
version = "0.1.0" | |||
authors = ["Stefan Bühler <stbuehler@web.de>"] | |||
edition = "2018" | |||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
[dependencies] |
@ -0,0 +1,2 @@ | |||
R1005,U563,R417,U509,L237,U555,R397,U414,L490,U336,L697,D682,L180,U951,L189,D547,R697,U583,L172,D859,L370,D114,L519,U829,R389,U608,R66,D634,L320,D49,L931,U137,L349,D689,L351,D829,R819,D138,L118,D849,R230,U858,L509,D311,R815,U217,R359,U840,R77,U230,R361,U322,R300,D646,R348,U815,R793,D752,R967,U128,R948,D499,R359,U572,L566,U815,R630,D290,L829,D736,R358,U778,R891,U941,R544,U889,L920,U913,L447,D604,R538,U818,L215,D437,R447,U576,R452,D794,R864,U269,L325,D35,L268,D639,L101,U777,L776,U958,R105,U517,R667,D423,R603,U469,L125,D919,R879,U994,R665,D377,R456,D570,L685,U291,R261,U846,R840,U418,L974,D270,L312,D426,R621,D334,L855,D378,R694,U845,R481,U895,L362,D840,L712,U57,R276,D643,R566,U348,R361,D144,L287,D864,L556,U610,L927,U322,R271,D90,L741,U446,R181,D527,R56,U805,L907,D406,L286,U873,L79,D280,L153,D377,R253,D61,R475,D804,R788,U393,L660,U314,R489,D491,L234,D712,L253,U651,L777,D726,R146,U47,R630,U517,R226,U624,L834,D153,L513,U799,R287,D868,R982,U390,L296,D373,R9,U994,R105,D673,L657,D868,R738,D277,R374,U828,R860,U247,R484,U986,L723,D847,L578,U487,L51,D865,L328,D199,R812,D726,R355,D463,R761,U69,R508,D753,L81,D50,L345,D66,L764,D466,L975,U619,R59,D788,L737,D360,R14,D253,L512,D417,R828,D188,L394,U212,R658,U369,R920,U927,L339,U552,R856,D458,R407,U41,L930,D460,R809,U467,L410,D800,L135,D596,R678,D4,L771,D637,L876,U192,L406,D136,R666,U730,R711,D291,L586,U845,R606,U2,L228,D759,R244,U946,R948,U160,R397,U134,R188,U850,R623,D315,L219,D450,R489,U374,R299,D474,L767,D679,L160,D403,L708 | |||
L1003,D878,R937,D979,R921,U572,R4,D959,L884,U394,R221,U206,R806,U912,R345,D290,R65,D996,L411,D157,R590,D557,L32,D360,L691,D861,L156,D603,R733,U444,L433,U144,L238,U213,R827,U949,R384,D409,L727,U923,L98,U781,L201,D200,R749,U288,L486,U158,L494,D522,R636,D330,L507,U691,R918,D706,R163,U609,R559,U674,R784,D87,R670,U401,L85,U981,R848,D579,L882,U777,R671,D385,R913,D899,R92,D780,L795,U821,R956,U446,L109,D955,L570,D874,R499,U845,R769,U88,L529,U657,R553,D357,L83,D324,L273,U689,L715,U933,R161,U561,L603,U349,L445,U781,R299,U26,L212,U429,R763,U116,R961,D258,L518,D668,L767,U587,L654,D24,R318,U35,L9,D199,L161,U419,R6,D707,R944,U499,R207,D349,L727,D637,R735,D137,R18,D214,L531,D327,L916,U440,R859,U483,R952,D631,L96,D320,L192,D985,R330,D196,L345,D575,L535,D868,R376,D126,R903,D619,L126,D624,L990,D67,L927,U685,L200,D759,L157,D816,L585,U910,R587,D598,L398,U706,R847,U682,L919,D291,L932,D54,L314,U430,L60,U206,L997,D487,L874,U957,L753,U999,R156,U102,L826,U923,L204,U293,L244,U787,L273,D687,R134,D167,L287,D459,R875,D32,R635,D400,L179,D19,L576,U60,L182,D409,R114,U329,R207,U525,L295,U305,L861,U280,R531,D49,L890,U521,L283,U37,R344,D867,L474,U893,R140,U289,L67,U490,R121,D34,L696,U902,R288,U249,R107,D750,R389,U125,L406,U950,R932,U795,R205,U583,L665,U214,R806,D409,R832,D39,R207,D977,L873,U645,L762,U847,L725,U397,R414,D558,L669,D736,R897,U464,R207,U359,R257,U304,L932,U240,L582,U409,L493,D481,R48,D537,R893,U48,R707,U630,L70,D289,L769,U98,L679,U504,L337,U117,L343,D574,R595,U168,R498 |
@ -0,0 +1,191 @@ | |||
type Error = Box<dyn std::error::Error>; | |||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)] | |||
pub enum Move { | |||
Right(u32), | |||
Left(u32), | |||
Up(u32), | |||
Down(u32), | |||
} | |||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)] | |||
pub enum Segment { | |||
Horizontal { | |||
row: i32, | |||
col_start: i32, | |||
col_end: i32, | |||
steps_start: i32, | |||
steps_dir: i32, | |||
}, | |||
Vertical { | |||
col: i32, | |||
row_start: i32, | |||
row_end: i32, | |||
steps_start: i32, | |||
steps_dir: i32, | |||
}, | |||
} | |||
impl Segment { | |||
pub fn collision(&self, other: &Self) -> Option<Position> { | |||
match (*self, *other) { | |||
(Segment::Horizontal {..}, Segment::Horizontal {..}) => None, | |||
(Segment::Vertical {..}, Segment::Vertical {..}) => None, | |||
( | |||
Segment::Horizontal { row, col_start, col_end, steps_start: col_steps_start, steps_dir: col_steps_dir }, | |||
Segment::Vertical { col, row_start, row_end, steps_start: row_steps_start, steps_dir: row_steps_dir }, | |||
) | |||
|( | |||
Segment::Vertical { col, row_start, row_end, steps_start: row_steps_start, steps_dir: row_steps_dir }, | |||
Segment::Horizontal { row, col_start, col_end, steps_start: col_steps_start, steps_dir: col_steps_dir }, | |||
) | |||
=> { | |||
if row_start <= row && row <= row_end && col_start <= col && col <= col_end { | |||
let col_steps = col_steps_start + (col - col_start) * col_steps_dir; | |||
let row_steps = row_steps_start + (row - row_start) * row_steps_dir; | |||
Some(Position { row, col, steps: col_steps + row_steps }) | |||
} else { | |||
None | |||
} | |||
} | |||
} | |||
} | |||
} | |||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] | |||
pub struct Position { | |||
row: i32, | |||
col: i32, | |||
steps: i32, | |||
} | |||
impl Position { | |||
pub fn manhatten_distance(&self) -> u32 { | |||
self.row.abs() as u32 + self.col.abs() as u32 | |||
} | |||
pub fn trace(&mut self, mov: Move) -> Segment { | |||
let start = *self; | |||
match mov { | |||
Move::Right(off) => { | |||
self.col += off as i32; | |||
self.steps += off as i32; | |||
Segment::Horizontal { | |||
row: start.row, | |||
col_start: start.col, | |||
col_end: self.col, | |||
steps_start: start.steps, | |||
steps_dir: 1, | |||
} | |||
}, | |||
Move::Left(off) => { | |||
self.col -= off as i32; | |||
self.steps += off as i32; | |||
Segment::Horizontal { | |||
row: start.row, | |||
col_start: self.col, | |||
col_end: start.col, | |||
steps_start: self.steps, | |||
steps_dir: -1, | |||
} | |||
}, | |||
Move::Up(off) => { | |||
self.row -= off as i32; | |||
self.steps += off as i32; | |||
Segment::Vertical { | |||
col: start.col, | |||
row_start: self.row, | |||
row_end: start.row, | |||
steps_start: self.steps, | |||
steps_dir: -1, | |||
} | |||
}, | |||
Move::Down(off) => { | |||
self.row += off as i32; | |||
self.steps += off as i32; | |||
Segment::Vertical { | |||
col: start.col, | |||
row_start: start.row, | |||
row_end: self.row, | |||
steps_start: start.steps, | |||
steps_dir: 1, | |||
} | |||
}, | |||
} | |||
} | |||
} | |||
impl std::str::FromStr for Move { | |||
type Err = Error; | |||
fn from_str(s: &str) -> Result<Self, Self::Err> { | |||
let e = move || Box::new(std::io::Error::new(std::io::ErrorKind::Other, "invalid move")); | |||
let n = s[1..].parse::<u32>()?; | |||
if s.is_empty() || !s.is_ascii() { | |||
return Err(e()); | |||
} | |||
Ok(match s.as_bytes()[0] { | |||
b'R' => Move::Right(n), | |||
b'L' => Move::Left(n), | |||
b'U' => Move::Up(n), | |||
b'D' => Move::Down(n), | |||
_ => return Err(e()), | |||
}) | |||
} | |||
} | |||
fn parse_moves<'a>(input: &'a str) -> impl Iterator<Item=Result<Move, Error>> + 'a { | |||
input.split(',').map(str::parse) | |||
} | |||
fn trace_moves<'a>(input: &'a str) -> impl Iterator<Item=Result<Segment, Error>> + 'a { | |||
let mut pos = Position::default(); | |||
parse_moves(input).map(move |m| m.map(|m| pos.trace(m))) | |||
} | |||
fn closest_intersection(lines: &[Vec<Segment>; 2]) -> u32 { | |||
lines[0][1..].iter().map(|a| lines[1][1..].iter().filter_map(move |b| a.collision(b).map(|p| p.manhatten_distance()))).flatten().min().unwrap() | |||
} | |||
fn shortest_delay(lines: &[Vec<Segment>; 2]) -> i32 { | |||
lines[0][1..].iter().map(|a| lines[1][1..].iter().filter_map(move |b| a.collision(b).map(|p| p.steps))).flatten().min().unwrap() | |||
} | |||
fn parse(input: &str) -> [Vec<Segment>; 2] { | |||
let mut lines = input.split_whitespace().map(trace_moves).map(Iterator::collect::<Result<Vec<_>, _>>); | |||
let result = [ | |||
lines.next().expect("require two lines").unwrap(), | |||
lines.next().expect("require two lines").unwrap(), | |||
]; | |||
assert!(lines.next().is_none(), "require only two lines"); | |||
result | |||
} | |||
fn main() { | |||
let input: &str = include_str!("input.txt"); | |||
let lines = parse(input); | |||
println!("Distance 1: {}", closest_intersection(&lines)); | |||
println!("Distance 2: {}", shortest_delay(&lines)); | |||
} | |||
#[cfg(test)] | |||
mod day3test { | |||
use super::*; | |||
#[test] | |||
fn examples1() { | |||
assert_eq!(closest_intersection(&parse("R75,D30,R83,U83,L12,D49,R71,U7,L72 | |||
U62,R66,U55,R34,D71,R55,D58,R83")), 159); | |||
assert_eq!(closest_intersection(&parse("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51 | |||
U98,R91,D20,R16,D67,R40,U7,R15,U6,R7")), 135); | |||
} | |||
#[test] | |||
fn examples2() { | |||
assert_eq!(shortest_delay(&parse("R75,D30,R83,U83,L12,D49,R71,U7,L72 | |||
U62,R66,U55,R34,D71,R55,D58,R83")), 610); | |||
assert_eq!(shortest_delay(&parse("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51 | |||
U98,R91,D20,R16,D67,R40,U7,R15,U6,R7")), 410); | |||
} | |||
} |
@ -0,0 +1,9 @@ | |||
[package] | |||
name = "day4" | |||
version = "0.1.0" | |||
authors = ["Stefan Bühler <stbuehler@web.de>"] | |||
edition = "2018" | |||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
[dependencies] |
@ -0,0 +1,80 @@ | |||
/* | |||
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 122345). | |||
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). | |||
223450 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 input is 206938-679128. | |||
*/ | |||
fn valid_password1(number: u32) -> bool { | |||
use std::io::Write; | |||
if number < 100_000 || number > 999_999 { return false; } | |||
let mut digits = [0u8; 6]; | |||
write!(&mut digits[..], "{}", number).unwrap(); | |||
let mut have_double_digit = false; | |||
for i in 0..5 { | |||
if digits[i] > digits[i+1] { return false; } | |||
if digits[i] == digits[i+1] { have_double_digit = true } | |||
} | |||
have_double_digit | |||
} | |||
fn valid_password2(number: u32) -> bool { | |||
use std::io::Write; | |||
if number < 100_000 || number > 999_999 { return false; } | |||
let mut digits = [0u8; 6]; | |||
write!(&mut digits[..], "{}", number).unwrap(); | |||
let mut have_double_digit = false; | |||
let mut current_run = 1; | |||
for i in 0..5 { | |||
if digits[i] > digits[i+1] { return false; } | |||
if digits[i] == digits[i+1] { | |||
current_run += 1; | |||
} else { | |||
if current_run == 2 { | |||
have_double_digit = true; | |||
} | |||
current_run = 1; | |||
} | |||
} | |||
if current_run == 2 { | |||
have_double_digit = true; | |||
} | |||
have_double_digit | |||
} | |||
fn main() { | |||
println!("Count 1: {}", (206938..=679128).filter(|n| valid_password1(*n)).count()); | |||
println!("Count 2: {}", (206938..=679128).filter(|n| valid_password2(*n)).count()); | |||
} | |||
#[cfg(test)] | |||
mod day4test { | |||
#[test] | |||
fn examples1() { | |||
assert!(super::valid_password1(111111)); | |||
assert!(!super::valid_password1(223450)); | |||
assert!(!super::valid_password1(123789)); | |||
} | |||
#[test] | |||
fn examples2() { | |||
assert!(super::valid_password2(112233)); | |||
assert!(!super::valid_password2(123444)); | |||
assert!(super::valid_password2(111122)); | |||
} | |||
} |
@ -0,0 +1,10 @@ | |||
[package] | |||
name = "day5" | |||
version = "0.1.0" | |||
authors = ["Stefan Bühler <stbuehler@web.de>"] | |||
edition = "2018" | |||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
[dependencies] | |||
shared = { path = "../shared" } |
@ -0,0 +1 @@ | |||
3,225,1,225,6,6,1100,1,238,225,104,0,1101,9,90,224,1001,224,-99,224,4,224,102,8,223,223,1001,224,6,224,1,223,224,223,1102,26,62,225,1101,11,75,225,1101,90,43,225,2,70,35,224,101,-1716,224,224,4,224,1002,223,8,223,101,4,224,224,1,223,224,223,1101,94,66,225,1102,65,89,225,101,53,144,224,101,-134,224,224,4,224,1002,223,8,223,1001,224,5,224,1,224,223,223,1102,16,32,224,101,-512,224,224,4,224,102,8,223,223,101,5,224,224,1,224,223,223,1001,43,57,224,101,-147,224,224,4,224,102,8,223,223,101,4,224,224,1,223,224,223,1101,36,81,225,1002,39,9,224,1001,224,-99,224,4,224,1002,223,8,223,101,2,224,224,1,223,224,223,1,213,218,224,1001,224,-98,224,4,224,102,8,223,223,101,2,224,224,1,224,223,223,102,21,74,224,101,-1869,224,224,4,224,102,8,223,223,1001,224,7,224,1,224,223,223,1101,25,15,225,1101,64,73,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,1008,226,677,224,1002,223,2,223,1005,224,329,1001,223,1,223,1007,677,677,224,102,2,223,223,1005,224,344,101,1,223,223,108,226,677,224,102,2,223,223,1006,224,359,101,1,223,223,108,226,226,224,1002,223,2,223,1005,224,374,1001,223,1,223,7,226,226,224,1002,223,2,223,1006,224,389,1001,223,1,223,8,226,677,224,1002,223,2,223,1006,224,404,1001,223,1,223,107,677,677,224,1002,223,2,223,1006,224,419,101,1,223,223,1008,677,677,224,102,2,223,223,1006,224,434,101,1,223,223,1107,226,677,224,102,2,223,223,1005,224,449,1001,223,1,223,107,226,226,224,102,2,223,223,1006,224,464,101,1,223,223,107,226,677,224,102,2,223,223,1005,224,479,1001,223,1,223,8,677,226,224,102,2,223,223,1005,224,494,1001,223,1,223,1108,226,677,224,102,2,223,223,1006,224,509,101,1,223,223,1107,677,226,224,1002,223,2,223,1005,224,524,101,1,223,223,1008,226,226,224,1002,223,2,223,1005,224,539,101,1,223,223,7,226,677,224,1002,223,2,223,1005,224,554,101,1,223,223,1107,677,677,224,1002,223,2,223,1006,224,569,1001,223,1,223,8,226,226,224,1002,223,2,223,1006,224,584,101,1,223,223,1108,677,677,224,102,2,223,223,1005,224,599,101,1,223,223,108,677,677,224,1002,223,2,223,1006,224,614,101,1,223,223,1007,226,226,224,102,2,223,223,1005,224,629,1001,223,1,223,7,677,226,224,1002,223,2,223,1005,224,644,101,1,223,223,1007,226,677,224,102,2,223,223,1005,224,659,1001,223,1,223,1108,677,226,224,102,2,223,223,1006,224,674,101,1,223,223,4,223,99,226 |
@ -0,0 +1,82 @@ | |||
use shared::intcode::{IntCode, SimulateStep, CELL}; | |||
fn run_air_conditioner(mut ic: IntCode) -> (Vec<CELL>, CELL) { | |||
let mut test_results = Vec::new(); | |||
ic.next_input = Some(1); // air conditioner unit ID | |||
loop { | |||
match ic.simulate_step() { | |||
SimulateStep::Finished => { | |||
let diagnostic = test_results.pop().expect("missing diagnostic code"); | |||
return (test_results, diagnostic); | |||
}, | |||
SimulateStep::Output(v) => test_results.push(v), | |||
SimulateStep::WaitForInput => panic!("AirConditioner already initialized, no further input"), | |||
} | |||
} | |||
} | |||
fn run_thermal_radiator_controller(mut ic: IntCode) -> CELL { | |||
ic.next_input = Some(5); // thermal radiator controller ID | |||
let diagnostic = ic.simulate_step().expect_output(); | |||
ic.simulate_step().expect_finished(); | |||
diagnostic | |||
} | |||
fn main() { | |||
let ic = include_str!("input.txt").parse::<IntCode>().unwrap(); | |||
println!("AirConditioner: {:?}", run_air_conditioner(ic.clone())); | |||
println!("ThermalRadiatorController: {:?}", run_thermal_radiator_controller(ic)); | |||
} | |||
#[cfg(test)] | |||
mod day5test { | |||
use super::*; | |||
fn parse(input: &str) -> IntCode { | |||
input.parse().unwrap() | |||
} | |||
fn run_single_io(code: &str, input: CELL) -> CELL { | |||
let mut ic = parse(code); | |||
ic.next_input = Some(input); | |||
let result = ic.simulate_step().expect_output(); | |||
ic.simulate_step().expect_finished(); | |||
result | |||
} | |||
#[test] | |||
fn examples1() { | |||
// no runnable examples | |||
} | |||
#[test] | |||
fn examples2() { | |||
// position mode: input is 8? | |||
assert_eq!(run_single_io("3,9,8,9,10,9,4,9,99,-1,8", 8), 1); | |||
assert_eq!(run_single_io("3,9,8,9,10,9,4,9,99,-1,8", 7), 0); | |||
assert_eq!(run_single_io("3,9,8,9,10,9,4,9,99,-1,8", 9), 0); | |||
// position mode: input less than 8? | |||
assert_eq!(run_single_io("3,9,7,9,10,9,4,9,99,-1,8", 8), 0); | |||
assert_eq!(run_single_io("3,9,7,9,10,9,4,9,99,-1,8", 7), 1); | |||
assert_eq!(run_single_io("3,9,7,9,10,9,4,9,99,-1,8", 9), 0); | |||
// immediate mode: input is 8? | |||
assert_eq!(run_single_io("3,3,1108,-1,8,3,4,3,99", 8), 1); | |||
assert_eq!(run_single_io("3,3,1108,-1,8,3,4,3,99", 7), 0); | |||
assert_eq!(run_single_io("3,3,1108,-1,8,3,4,3,99", 9), 0); | |||
// immediate mode: input less than 8? | |||
assert_eq!(run_single_io("3,3,1107,-1,8,3,4,3,99", 8), 0); | |||
assert_eq!(run_single_io("3,3,1107,-1,8,3,4,3,99", 7), 1); | |||
assert_eq!(run_single_io("3,3,1107,-1,8,3,4,3,99", 9), 0); | |||
// position mode: (bool) input | |||
assert_eq!(run_single_io("3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9", 0), 0); | |||
assert_eq!(run_single_io("3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9", 1), 1); | |||
assert_eq!(run_single_io("3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9", 999), 1); | |||
assert_eq!(run_single_io("3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9", -1), 1); | |||
// immediate mode: (bool) input | |||
assert_eq!(run_single_io("3,3,1105,-1,9,1101,0,0,12,4,12,99,1", 0), 0); | |||
assert_eq!(run_single_io("3,3,1105,-1,9,1101,0,0,12,4,12,99,1", 1), 1); | |||
assert_eq!(run_single_io("3,3,1105,-1,9,1101,0,0,12,4,12,99,1", 999), 1); | |||
assert_eq!(run_single_io("3,3,1105,-1,9,1101,0,0,12,4,12,99,1", -1), 1); | |||
} | |||
} |
@ -0,0 +1,9 @@ | |||
[package] | |||
name = "day6" | |||
version = "0.1.0" | |||
authors = ["Stefan Bühler <stbuehler@web.de>"] | |||
edition = "2018" | |||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
[dependencies] |
@ -0,0 +1,87 @@ | |||
use std::cell::Cell; | |||
use std::collections::{HashMap, HashSet}; | |||
fn parse(s: &str) -> impl Iterator<Item=(&str, &str)> { | |||
s.split_ascii_whitespace().map(|l| { | |||
let mut x = l.splitn(2, ')'); | |||
let a = x.next().unwrap(); | |||
let b = x.next().unwrap(); | |||
(a, b) | |||
}) | |||
} | |||
fn count_orbits_get(orbit_relations: &HashMap<&str, (&str, Cell<Option<u32>>)>, object: &str) -> u32 { | |||
if object == "COM" { return 0; } | |||
let around = &orbit_relations[object]; | |||
if let Some(n) = around.1.get() { return n; } | |||
let n = count_orbits_get(orbit_relations, around.0) + 1; | |||
around.1.set(Some(n)); | |||
n | |||
} | |||
fn count_orbits(input: &str) -> u32 { | |||
let orbit_relations: HashMap<&str, (&str, Cell<Option<u32>>)> = parse(input).map(|(a, b)| (b, (a, Cell::new(None)))).collect(); | |||
let mut sum = 0; | |||
for (_object, around) in &orbit_relations { | |||
if let Some(n) = around.1.get() { | |||
sum += n; | |||
} else { | |||
let n = count_orbits_get(&orbit_relations, around.0) + 1; | |||
around.1.set(Some(n)); | |||
sum += n; | |||
} | |||
} | |||
// println!("Map: {:?}", orbit_relations); | |||
sum | |||
} | |||
fn count_orbit_transfers(input: &str) -> u32 { | |||
let orbit_relations: HashMap<&str, (&str, Cell<Option<u32>>)> = parse(input).map(|(a, b)| (b, (a, Cell::new(None)))).collect(); | |||
for (_object, around) in &orbit_relations { | |||
if around.1.get().is_none() { | |||
let n = count_orbits_get(&orbit_relations, around.0) + 1; | |||
around.1.set(Some(n)); | |||
} | |||
} | |||
// println!("Map: {:?}", orbit_relations); | |||
let mut you = HashSet::new(); | |||
let mut object = "YOU"; | |||
loop { | |||
if "COM" == object { break; } | |||
object = orbit_relations[object].0; | |||
// println!("YOU orbit (...) {}", object); | |||
you.insert(object); | |||
} | |||
object = "SAN"; | |||
loop { | |||
if you.contains(&object) { | |||
let you = orbit_relations["YOU"].1.get().unwrap(); | |||
let san = orbit_relations["SAN"].1.get().unwrap(); | |||
let bottom = orbit_relations[object].1.get().unwrap(); | |||
return (you - bottom - 1) + (san - bottom - 1); | |||
} | |||
object = orbit_relations[object].0; | |||
// println!("SAN orbit (...) {}", object); | |||
} | |||
} | |||
fn main() { | |||
let input = include_str!("input.txt"); | |||
println!("Orbit sum: {}", count_orbits(input)); | |||
println!("Orbit transfers: {}", count_orbit_transfers(input)); | |||
} | |||
#[cfg(test)] | |||
mod day6test { | |||
use super::*; | |||
#[test] | |||
fn examples1() { | |||
assert_eq!(count_orbits("COM)B B)C C)D D)E E)F B)G G)H D)I E)J J)K K)L"), 42); | |||
} | |||
#[test] | |||
fn examples2() { | |||
assert_eq!(count_orbit_transfers("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"), 4); | |||
} | |||
} |
@ -0,0 +1,11 @@ | |||
[package] | |||
name = "day7" | |||
version = "0.1.0" | |||
authors = ["Stefan Bühler <stbuehler@web.de>"] | |||
edition = "2018" | |||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
[dependencies] | |||
itertools = "0.8.2" | |||
shared = { path = "../shared" } |
@ -0,0 +1 @@ | |||
3,8,1001,8,10,8,105,1,0,0,21,34,51,64,73,98,179,260,341,422,99999,3,9,102,4,9,9,1001,9,4,9,4,9,99,3,9,1001,9,4,9,1002,9,3,9,1001,9,5,9,4,9,99,3,9,101,5,9,9,102,5,9,9,4,9,99,3,9,101,5,9,9,4,9,99,3,9,1002,9,5,9,1001,9,3,9,102,2,9,9,101,5,9,9,1002,9,2,9,4,9,99,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,1001,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,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,99,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,99,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,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,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,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,2,9,9,4,9,3,9,1001,9,2,9,4,9,99,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,99 |
@ -0,0 +1,94 @@ | |||
use shared::intcode::{IntCode, SimulateStep, CELL}; | |||
pub fn run_stages(ic: &IntCode, phases: &[CELL]) -> CELL { | |||
let mut value = 0; | |||
for ndx in 0..5 { | |||
let mut stage = ic.clone(); | |||
stage.next_input = Some(phases[ndx]); | |||
stage.simulate_step().expect_wait_for_input(); | |||
stage.next_input = Some(value); | |||
value = stage.simulate_step().expect_output(); | |||
stage.simulate_step().expect_finished(); | |||
} | |||
value | |||
} | |||
pub fn run_feedback_stages(ic: &IntCode, phases: &[CELL]) -> CELL { | |||
let mut stages = [ ic.clone(), ic.clone(), ic.clone(), ic.clone(), ic.clone() ]; | |||
// init all with phase | |||
for ndx in 0..5 { | |||
stages[ndx].next_input = Some(phases[ndx]); | |||
stages[ndx].simulate_step().expect_wait_for_input(); | |||
} | |||
let mut output = 0; | |||
stages[0].next_input = Some(output); | |||
let mut current_ndx = 0; | |||
loop { | |||
match stages[current_ndx].simulate_step() { | |||
SimulateStep::Output(v) => { | |||
current_ndx = (current_ndx + 1) % 5; | |||
stages[current_ndx].next_input = Some(v); | |||
if current_ndx == 0 { | |||
// remember last final output | |||
output = v; | |||
} | |||
}, | |||
SimulateStep::WaitForInput => { | |||
panic!("Can't wait for new input before generating one"); | |||
}, | |||
SimulateStep::Finished => break, | |||
} | |||
} | |||
assert_eq!(current_ndx, 0, "first amplifiert should be first to stop"); | |||
for ndx in current_ndx+1..5 { | |||
stages[ndx].simulate_step().expect_finished(); | |||
} | |||
output | |||
} | |||
pub fn best_amplifier(ic: &IntCode) -> (Vec<CELL>, CELL) { | |||
use itertools::Itertools; | |||
(0..5).permutations(5).map(|phases| { | |||
let value = run_stages(ic, &phases); | |||
(phases, value) | |||
}).max_by_key(|(_, value)| *value).unwrap() | |||
} | |||
pub fn best_feedback_amplifier(ic: &IntCode) -> (Vec<CELL>, CELL) { | |||
use itertools::Itertools; | |||
(5..10).permutations(5).map(|phases| { | |||
let value = run_feedback_stages(ic, &phases); | |||
(phases, value) | |||
}).max_by_key(|(_, value)| *value).unwrap() | |||
} | |||
fn main() { | |||
let ic = include_str!("input.txt").parse::<IntCode>().unwrap(); | |||
let (phases, max_value) = best_amplifier(&ic); | |||
println!("Max Value: {} (from phases: {:?}", max_value, phases); | |||
let (phases, max_value) = best_feedback_amplifier(&ic); | |||
println!("Max Feedback Value: {} (from phases: {:?}", max_value, phases); | |||
} | |||
#[cfg(test)] | |||
mod day7test { | |||
use super::*; | |||
#[test] | |||
fn examples1() { | |||
assert_eq!(best_amplifier(&"3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0".parse::<IntCode>().unwrap()), (vec![4,3,2,1,0], 43210)); | |||
assert_eq!(best_amplifier(&"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".parse::<IntCode>().unwrap()), (vec![0,1,2,3,4], 54321)); | |||
assert_eq!(best_amplifier(&"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".parse::<IntCode>().unwrap()), (vec![1,0,4,3,2], 65210)); | |||
} | |||
#[test] | |||
fn examples2() { | |||
assert_eq!(best_feedback_amplifier(&"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".parse::<IntCode>().unwrap()), (vec![9,8,7,6,5], 139629729)); | |||
assert_eq!(best_feedback_amplifier(&"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".parse::<IntCode>().unwrap()), (vec![9,7,8,5,6], 18216)); | |||
} | |||
} |
@ -0,0 +1,9 @@ | |||
[package] | |||
name = "shared" | |||
version = "0.1.0" | |||
authors = ["Stefan Bühler <stbuehler@web.de>"] | |||
edition = "2018" | |||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
[dependencies] |
@ -0,0 +1,148 @@ | |||
const OP_ADD: CELL = 1; | |||
const OP_MUL: CELL = 2; | |||
const OP_INPUT: CELL = 3; | |||
const OP_OUTPUT: CELL = 4; | |||
const OP_JUMP_IF_TRUE: CELL = 5; | |||
const OP_JUMP_IF_FALSE: CELL = 6; | |||
const OP_LESS_THAN: CELL = 7; | |||
const OP_EQUAL: CELL = 8; | |||
const OP_HALT: CELL = 99; | |||
pub type CELL = i64; | |||
type Mode = u8; | |||
#[derive(PartialEq, Eq, Clone, Debug)] | |||
pub enum SimulateStep { | |||
Finished, | |||
WaitForInput, | |||
Output(CELL), | |||
} | |||
impl SimulateStep { | |||
pub fn expect_output(self) -> CELL { | |||
if let Self::Output(v) = self { | |||
return v; | |||
} | |||
panic!("Expected Output, got {:?}", self); | |||
} | |||
pub fn expect_finished(self) { | |||
if let Self::Finished = self { return; } | |||
panic!("Expected Finished, got {:?}", self); | |||
} | |||
pub fn expect_wait_for_input(self) { | |||
if let Self::WaitForInput = self { return; } | |||
panic!("Expected WaitForInput, got {:?}", self); | |||
} | |||
} | |||
#[derive(PartialEq, Eq, Clone, Debug)] | |||
pub struct IntCode { | |||
pub ip: usize, | |||
pub next_input: Option<CELL>, | |||
pub data: Vec<CELL>, | |||
} | |||
impl IntCode { | |||
#[inline(always)] | |||
fn fetch(&mut self, mode: Mode) -> CELL { | |||
let imm = self.data[self.ip]; | |||
self.ip += 1; | |||
match mode { | |||
1 => imm, | |||
_ => self.data[imm as usize], | |||
} | |||
} | |||
#[inline(always)] | |||
fn store(&mut self, mode: Mode, value: CELL) { | |||
let addr = self.ip; | |||
self.ip += 1; | |||
match mode { | |||
1 => self.data[addr] = value, | |||
_ => { | |||
let addr = self.data[addr] as usize; | |||
self.data[addr] = value; | |||
}, | |||
} | |||
} | |||
fn binop<F>(&mut self, op: F, modes: [Mode; 3]) | |||
where | |||
F: FnOnce(CELL, CELL) -> CELL, | |||
{ | |||
let src1 = self.fetch(modes[0]); | |||
let src2 = self.fetch(modes[1]); | |||
self.store(modes[2], op(src1, src2)); | |||
} | |||
pub fn simulate(&mut self) { | |||
assert_eq!(self.ip, 0); | |||
assert!(self.next_input.is_none()); | |||
self.simulate_step().expect_finished(); | |||
} | |||
pub fn simulate_step(&mut self) -> SimulateStep { | |||
loop { | |||
let modes = self.data[self.ip]; | |||
self.ip += 1; | |||
let op = modes % 100; | |||
let modes = modes / 100; | |||
let mode1 = (modes % 10) as Mode; | |||
let modes = modes / 10; | |||
let mode2 = (modes % 10) as Mode; | |||
let modes = modes / 10; | |||
let mode3 = (modes % 10) as Mode; | |||
let modes = [mode1, mode2, mode3]; | |||
match op { | |||
OP_ADD => self.binop(|a, b| a + b, modes), | |||
OP_MUL => self.binop(|a, b| a * b, modes), | |||
OP_LESS_THAN => self.binop(|a, b| if a < b { 1 } else { 0 }, modes), | |||
OP_EQUAL => self.binop(|a, b| if a == b { 1 } else { 0 }, modes), | |||
OP_INPUT => { | |||
if let Some(v) = self.next_input.take() { | |||
self.store(mode1, v); | |||
} else { | |||
self.ip -= 1; // go back to opcode | |||
return SimulateStep::WaitForInput; | |||
} | |||
}, | |||
OP_OUTPUT => { | |||
return SimulateStep::Output(self.fetch(mode1)); | |||
}, | |||
OP_HALT => { | |||
self.ip -= 1; // make re-running this step end up here again | |||
return SimulateStep::Finished; | |||
}, | |||
OP_JUMP_IF_TRUE => { | |||
if self.fetch(mode1) != 0 { | |||
self.ip = self.fetch(mode2) as usize; | |||
} else { | |||
self.ip += 1; // skip target | |||
} | |||
}, | |||
OP_JUMP_IF_FALSE => { | |||
if self.fetch(mode1) == 0 { | |||
self.ip = self.fetch(mode2) as usize; | |||
} else { | |||
self.ip += 1; // skip target | |||
} | |||
}, | |||
_ => panic!("invalid opcode: {}", op), | |||
} | |||
} | |||
} | |||
} | |||
impl std::str::FromStr for IntCode { | |||
type Err = <CELL as std::str::FromStr>::Err; | |||
fn from_str(s: &str) -> Result<Self, Self::Err> { | |||
Ok(IntCode { | |||
ip: 0, | |||
next_input: None, | |||
data: s.split(',').map(|s| s.trim().parse::<CELL>()).collect::<Result<_, _>>()?, | |||
}) | |||
} | |||
} |
@ -0,0 +1 @@ | |||
pub mod intcode; |