add day 2 to 7

This commit is contained in:
Stefan Bühler 2019-12-07 11:20:57 +01:00
parent 8c30eb1099
commit f7d70ba76e
23 changed files with 2709 additions and 2 deletions

54
Cargo.lock generated
View File

@ -4,3 +4,57 @@
name = "day1"
version = "0.1.0"
[[package]]
name = "day2"
version = "0.1.0"
dependencies = [
"shared 0.1.0",
]
[[package]]
name = "day3"
version = "0.1.0"
[[package]]
name = "day4"
version = "0.1.0"
[[package]]
name = "day5"
version = "0.1.0"
dependencies = [
"shared 0.1.0",
]
[[package]]
name = "day6"
version = "0.1.0"
[[package]]
name = "day7"
version = "0.1.0"
dependencies = [
"itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"shared 0.1.0",
]
[[package]]
name = "either"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itertools"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "shared"
version = "0.1.0"
[metadata]
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"

View File

@ -1,4 +1,11 @@
[workspace]
members = [
"day1",
"day2",
"day3",
"day4",
"day5",
"day6",
"day7",
"shared",
]

View File

@ -31,7 +31,7 @@ fn main() {
#[cfg(test)]
mod day1test {
#[test]
fn example1() {
fn examples1() {
// Original descriptions:
// > 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.
@ -44,7 +44,7 @@ mod day1test {
}
#[test]
fn example2() {
fn examples2() {
// Original descriptions:
// > 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.

10
day2/Cargo.toml Normal file
View File

@ -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" }

1
day2/src/input.txt Normal file
View File

@ -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

60
day2/src/main.rs Normal file
View File

@ -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
}
}

9
day3/Cargo.toml Normal file
View File

@ -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]

2
day3/src/input.txt Normal file
View File

@ -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

191
day3/src/main.rs Normal file
View File

@ -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);
}
}

9
day4/Cargo.toml Normal file
View File

@ -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]

80
day4/src/main.rs Normal file
View File

@ -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));
}
}

10
day5/Cargo.toml Normal file
View File

@ -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" }

1
day5/src/input.txt Normal file
View File

@ -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

82
day5/src/main.rs Normal file
View File

@ -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);
}
}

9
day6/Cargo.toml Normal file
View File

@ -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]

1831
day6/src/input.txt Normal file

File diff suppressed because it is too large Load Diff

87
day6/src/main.rs Normal file
View File

@ -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);
}
}

11
day7/Cargo.toml Normal file
View File

@ -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" }

1
day7/src/input.txt Normal file
View File

@ -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

94
day7/src/main.rs Normal file
View File

@ -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));
}
}

9
shared/Cargo.toml Normal file
View File

@ -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]

148
shared/src/intcode.rs Normal file
View File

@ -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<_, _>>()?,
})
}
}

1
shared/src/lib.rs Normal file
View File

@ -0,0 +1 @@
pub mod intcode;