Day 10&11

This commit is contained in:
Malte Tammena 2022-12-11 18:08:28 +01:00
parent 3eaea9577a
commit afc1255c76
12 changed files with 915 additions and 27 deletions

3
.gitignore vendored
View file

@ -1,4 +1,5 @@
/target
result
result*
flamegraph.svg
.direnv
perf.data*

44
Cargo.lock generated
View file

@ -8,6 +8,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dbdbb2877d26e0647c6b8125802b2ecf2dc2a28d864dde41e6f9b9a54da08fe"
[[package]]
name = "beef"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -143,6 +149,9 @@ version = "0.1.0"
[[package]]
name = "day11"
version = "0.1.0"
dependencies = [
"logos",
]
[[package]]
name = "day12"
@ -217,6 +226,12 @@ dependencies = [
"libc",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "getrandom"
version = "0.2.8"
@ -312,6 +327,29 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
[[package]]
name = "logos"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf8b031682c67a8e3d5446840f9573eb7fe26efe7ec8d195c9ac4c0647c502f1"
dependencies = [
"logos-derive",
]
[[package]]
name = "logos-derive"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d849148dbaf9661a6151d1ca82b13bb4c4c128146a88d05253b38d4e2f496c"
dependencies = [
"beef",
"fnv",
"proc-macro2",
"quote",
"regex-syntax",
"syn",
]
[[package]]
name = "numtoa"
version = "0.1.0"
@ -426,6 +464,12 @@ dependencies = [
"redox_syscall",
]
[[package]]
name = "regex-syntax"
version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "rustix"
version = "0.36.4"

View file

@ -4,16 +4,8 @@ use std::{
io::{BufRead, BufReader},
};
fn either_calories_or_blank<S: AsRef<str>>(inp: S) -> Option<usize> {
inp.as_ref().parse().ok()
}
#[derive(Default)]
struct TopThree {
first: usize,
second: usize,
third: usize,
}
struct TopThree([usize; 3]);
#[derive(Default)]
struct TopCal {
@ -38,24 +30,36 @@ impl TopCal {
impl TopThree {
const fn push_sum(self, sum: usize) -> Self {
let (first, second, third) = if sum > self.first {
(sum, self.first, self.second)
} else if sum > self.second {
(self.first, sum, self.second)
} else if sum > self.third {
(self.first, self.second, sum)
let new_order = if sum > self.first() {
[sum, self.first(), self.second()]
} else if sum > self.second() {
[self.first(), sum, self.second()]
} else if sum > self.third() {
[self.first(), self.second(), sum]
} else {
(self.first, self.second, self.third)
[self.first(), self.second(), self.third()]
};
Self {
first,
second,
third,
}
Self(new_order)
}
const fn total_sum(&self) -> usize {
self.first + self.second + self.third
self.first() + self.second() + self.third()
}
const fn nth<const N: usize>(&self) -> usize {
self.0[N]
}
const fn first(&self) -> usize {
self.nth::<0>()
}
const fn second(&self) -> usize {
self.nth::<1>()
}
const fn third(&self) -> usize {
self.nth::<2>()
}
}
fn either_calories_or_blank<S: AsRef<str>>(inp: S) -> Option<usize> {
inp.as_ref().parse().ok()
}
fn main() {

137
day10/input Normal file
View file

@ -0,0 +1,137 @@
noop
addx 7
addx -1
addx -1
addx 5
noop
noop
addx 1
addx 3
addx 2
noop
addx 2
addx 5
addx 2
addx 10
addx -9
addx 4
noop
noop
noop
addx 3
addx 5
addx -40
addx 26
addx -23
addx 2
addx 5
addx 26
addx -35
addx 12
addx 2
addx 17
addx -10
addx 3
noop
addx 2
addx 3
noop
addx 2
addx 3
noop
addx 2
addx 2
addx -39
noop
addx 15
addx -12
addx 2
addx 10
noop
addx -1
addx -2
noop
addx 5
noop
addx 5
noop
noop
addx 1
addx 4
addx -25
addx 26
addx 2
addx 5
addx 2
noop
addx -3
addx -32
addx 1
addx 4
addx -2
addx 3
noop
noop
addx 3
noop
addx 6
addx -17
addx 27
addx -7
addx 5
addx 2
addx 3
addx -2
addx 4
noop
noop
addx 5
addx 2
addx -39
noop
noop
addx 2
addx 5
addx 3
addx -2
addx 2
addx 11
addx -4
addx -5
noop
addx 10
addx -18
addx 19
addx 2
addx 5
addx 2
addx 2
addx 3
addx -2
addx 2
addx -37
noop
addx 5
addx 4
addx -1
noop
addx 4
noop
noop
addx 1
addx 4
noop
addx 1
addx 2
noop
addx 3
addx 5
noop
addx -3
addx 5
addx 5
addx 2
addx 3
noop
addx -32
noop

146
day10/input.test Normal file
View file

@ -0,0 +1,146 @@
addx 15
addx -11
addx 6
addx -3
addx 5
addx -1
addx -8
addx 13
addx 4
noop
addx -1
addx 5
addx -1
addx 5
addx -1
addx 5
addx -1
addx 5
addx -1
addx -35
addx 1
addx 24
addx -19
addx 1
addx 16
addx -11
noop
noop
addx 21
addx -15
noop
noop
addx -3
addx 9
addx 1
addx -3
addx 8
addx 1
addx 5
noop
noop
noop
noop
noop
addx -36
noop
addx 1
addx 7
noop
noop
noop
addx 2
addx 6
noop
noop
noop
noop
noop
addx 1
noop
noop
addx 7
addx 1
noop
addx -13
addx 13
addx 7
noop
addx 1
addx -33
noop
noop
noop
addx 2
noop
noop
noop
addx 8
noop
addx -1
addx 2
addx 1
noop
addx 17
addx -9
addx 1
addx 1
addx -3
addx 11
noop
noop
addx 1
noop
addx 1
noop
noop
addx -13
addx -19
addx 1
addx 3
addx 26
addx -30
addx 12
addx -1
addx 3
addx 1
noop
noop
noop
addx -9
addx 18
addx 1
addx 2
noop
noop
addx 9
noop
noop
noop
addx -1
addx 2
addx -37
addx 1
addx 3
noop
addx 15
addx -21
addx 22
addx -6
addx 1
noop
addx 2
addx 1
noop
addx -10
noop
noop
addx 20
addx 1
addx 2
addx 2
addx -6
addx -11
noop
noop
noop

View file

@ -1,3 +1,134 @@
fn main() {
println!("Hello, world!");
use std::{
fs::File,
io::{BufRead, BufReader},
};
const INITIAL_REG_VALUE: i32 = 1;
#[derive(Debug, Default, PartialEq, Eq)]
enum SolvePuzzle {
First,
#[default]
Second,
}
#[derive(Debug, Clone)]
enum Instruction {
AddX(i8),
Noop,
}
struct CRT {
curr_drawing: usize,
screen: [bool; 240],
}
impl CRT {
pub fn draw(&mut self, reg: i32) {
let horizontal_position = self.curr_drawing % 40;
if (reg - 1..=reg + 1).contains(&(horizontal_position as i32)) {
self.screen[self.curr_drawing] = true;
}
self.curr_drawing += 1;
}
}
impl Instruction {
pub fn parse<S: AsRef<str>>(line: S) -> Self {
let line = line.as_ref();
if let Some(value) = line.strip_prefix("addx ") {
Self::AddX(value.parse().unwrap_or_default())
} else {
Self::Noop
}
}
}
fn apply_instruction_to_register(
reg: &mut i32,
inst: Instruction,
) -> Option<impl Iterator<Item = i32>> {
match inst {
Instruction::AddX(value) => {
let old = *reg;
*reg += value as i32;
Some(::std::iter::repeat(old).take(2))
}
Instruction::Noop => Some(::std::iter::repeat(*reg).take(1)),
}
}
fn solve_puzzle<R: BufRead>(reader: R) -> i32 {
let mut register_history = reader
.lines()
.map(Result::unwrap)
.map(Instruction::parse)
.scan(INITIAL_REG_VALUE, apply_instruction_to_register)
.flatten();
[19_usize, 39, 39, 39, 39, 39]
.into_iter()
.fold((0_i32, 0_usize), |(mut sum, mut idx_sum), idx| {
idx_sum += idx as usize + 1;
sum += register_history.nth(idx).unwrap() * idx_sum as i32;
(sum, idx_sum)
})
.0
}
fn solve_second_puzzle<R: BufRead>(reader: R) {
let crt: CRT = reader
.lines()
.map(Result::unwrap)
.map(Instruction::parse)
.scan(INITIAL_REG_VALUE, apply_instruction_to_register)
.flatten()
.collect();
for y_pos in 0..6 {
let start = 40 * y_pos;
let end = 40 * (y_pos + 1);
let output: String = crt.screen[start..end]
.iter()
.map(|is_on| match is_on {
true => "X",
false => ".",
})
.collect();
println!("{output}");
}
}
fn main() {
let mut args = ::std::env::args().skip(1);
let file = args.next().unwrap_or_else(|| String::from("./input"));
let solve = args
.next()
.map(|arg| match arg.as_str() {
"first" => SolvePuzzle::First,
"second" => SolvePuzzle::Second,
_ => unreachable!(),
})
.unwrap_or_default();
let reader = BufReader::new(File::open(file).expect("Opening file"));
match solve {
SolvePuzzle::First => {
let solution = solve_puzzle(reader);
println!("{}", solution);
}
SolvePuzzle::Second => solve_second_puzzle(reader),
};
}
impl FromIterator<i32> for CRT {
fn from_iter<T: IntoIterator<Item = i32>>(iter: T) -> Self {
iter.into_iter().fold(
CRT {
curr_drawing: 0,
screen: [false; 240],
},
|mut crt, reg| {
crt.draw(reg);
crt
},
)
}
}

View file

@ -8,3 +8,4 @@ build = true
app = true
[dependencies]
logos = "0.12.1"

55
day11/input Normal file
View file

@ -0,0 +1,55 @@
Monkey 0:
Starting items: 74, 73, 57, 77, 74
Operation: new = old * 11
Test: divisible by 19
If true: throw to monkey 6
If false: throw to monkey 7
Monkey 1:
Starting items: 99, 77, 79
Operation: new = old + 8
Test: divisible by 2
If true: throw to monkey 6
If false: throw to monkey 0
Monkey 2:
Starting items: 64, 67, 50, 96, 89, 82, 82
Operation: new = old + 1
Test: divisible by 3
If true: throw to monkey 5
If false: throw to monkey 3
Monkey 3:
Starting items: 88
Operation: new = old * 7
Test: divisible by 17
If true: throw to monkey 5
If false: throw to monkey 4
Monkey 4:
Starting items: 80, 66, 98, 83, 70, 63, 57, 66
Operation: new = old + 4
Test: divisible by 13
If true: throw to monkey 0
If false: throw to monkey 1
Monkey 5:
Starting items: 81, 93, 90, 61, 62, 64
Operation: new = old + 7
Test: divisible by 7
If true: throw to monkey 1
If false: throw to monkey 4
Monkey 6:
Starting items: 69, 97, 88, 93
Operation: new = old * old
Test: divisible by 5
If true: throw to monkey 7
If false: throw to monkey 2
Monkey 7:
Starting items: 59, 80
Operation: new = old + 6
Test: divisible by 11
If true: throw to monkey 2
If false: throw to monkey 3

27
day11/input.test Normal file
View file

@ -0,0 +1,27 @@
Monkey 0:
Starting items: 79, 98
Operation: new = old * 19
Test: divisible by 23
If true: throw to monkey 2
If false: throw to monkey 3
Monkey 1:
Starting items: 54, 65, 75, 74
Operation: new = old + 6
Test: divisible by 19
If true: throw to monkey 2
If false: throw to monkey 0
Monkey 2:
Starting items: 79, 60, 97
Operation: new = old * old
Test: divisible by 13
If true: throw to monkey 1
If false: throw to monkey 3
Monkey 3:
Starting items: 74
Operation: new = old + 3
Test: divisible by 17
If true: throw to monkey 0
If false: throw to monkey 1

View file

@ -1,3 +1,60 @@
fn main() {
println!("Hello, world!");
use monkey_business::MonkeyBusiness;
mod monkey_business;
type WorryLevel = u64;
#[derive(Debug, Default, PartialEq, Eq)]
enum SolvePuzzle {
First,
#[default]
Second,
}
fn solve_puzzle<const ROUNDS: usize, const DIVIDE_WORRY_LEVEL: bool>(file: &str) -> usize {
let mut monkey_business = MonkeyBusiness::parse(file).expect("Parsing file");
for _round in 0..ROUNDS {
monkey_business.play_round::<DIVIDE_WORRY_LEVEL>();
}
monkey_business.into_level_of_monkey_business()
}
fn main() {
let mut args = ::std::env::args().skip(1);
let file = args.next().unwrap_or_else(|| String::from("./input"));
let solve = args
.next()
.map(|arg| match arg.as_str() {
"first" => SolvePuzzle::First,
"second" => SolvePuzzle::Second,
_ => unreachable!(),
})
.unwrap_or_default();
let file = ::std::fs::read_to_string(file).expect("Reading file");
let solution = match solve {
SolvePuzzle::First => solve_puzzle::<20, true>(&file),
SolvePuzzle::Second => solve_puzzle::<10_000, false>(&file),
};
println!("{}", solution);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn example_on_first() {
assert_eq!(
solve_puzzle::<20, true>(include_str!("../input.test")),
10_605
);
}
#[test]
fn example_on_second() {
assert_eq!(
solve_puzzle::<10_000, false>(include_str!("../input.test")),
2713310158
);
}
}

View file

@ -0,0 +1,80 @@
use std::cmp::Reverse;
use crate::WorryLevel;
mod parser;
#[derive(Debug)]
pub enum Operation {
Add(WorryLevel),
Mul(WorryLevel),
Square,
}
impl Operation {
fn apply_to(&self, item: &mut WorryLevel, lcm: &WorryLevel) {
match self {
Operation::Add(num) => *item = (*item + num) % lcm,
Operation::Mul(num) => *item = (*item * num) % lcm,
Operation::Square => *item = (*item * *item) % lcm,
}
}
}
#[derive(Debug)]
pub struct Monkey {
holding: Vec<WorryLevel>,
operation: Operation,
test_div_by: WorryLevel,
on_test_true_target: usize,
on_test_false_target: usize,
inspection_count: usize,
}
#[derive(Debug)]
pub struct MonkeyBusiness {
monkeys: Vec<Monkey>,
lcm: WorryLevel,
}
impl MonkeyBusiness {
pub fn play_round<const DIVIDE_WORRY_LEVEL: bool>(&mut self) {
for monkey_idx in 0..self.monkeys.len() {
let new_monkey_idxs =
self.monkeys[monkey_idx].play_round::<DIVIDE_WORRY_LEVEL>(self.lcm);
let mut holding = Vec::with_capacity(self.monkeys[monkey_idx].holding.len());
::std::mem::swap(&mut holding, &mut self.monkeys[monkey_idx].holding);
for (monkey_idx, item) in new_monkey_idxs.into_iter().zip(holding) {
self.monkeys[monkey_idx].holding.push(item);
}
}
}
pub fn into_level_of_monkey_business(mut self) -> usize {
self.monkeys
.sort_by_key(|monkey| Reverse(monkey.inspection_count));
self.monkeys
.into_iter()
.take(2)
.map(|monkey| monkey.inspection_count)
.product()
}
}
impl Monkey {
pub fn play_round<const DIVIDE_WORRY_LEVEL: bool>(&mut self, lcm: WorryLevel) -> Vec<usize> {
let mut new_monkeys = Vec::with_capacity(self.holding.len());
for item in &mut self.holding {
self.inspection_count += 1;
self.operation.apply_to(item, &lcm);
if DIVIDE_WORRY_LEVEL {
*item = *item / 3 % lcm;
}
if *item % self.test_div_by == 0 {
new_monkeys.push(self.on_test_true_target);
} else {
new_monkeys.push(self.on_test_false_target);
}
}
new_monkeys
}
}

View file

@ -0,0 +1,205 @@
use logos::{Lexer, Logos, Span};
use crate::WorryLevel;
use super::{Monkey, MonkeyBusiness, Operation};
#[derive(Debug, Logos, PartialEq, Eq)]
pub enum Token {
#[token("Monkey")]
Monkey,
#[regex("[0-9]+", |lex| lex.slice().parse())]
Number(WorryLevel),
#[token(":")]
Colon,
#[token("Starting items")]
StartingItems,
#[token(",")]
Comma,
#[token("Operation")]
Operation,
#[token("new")]
New,
#[token("=")]
EqualSign,
#[token("old")]
Old,
#[token("*")]
Multiplication,
#[token("+")]
Addition,
#[token("Test")]
Test,
#[token("divisible by")]
DivisibleBy,
#[token("If true")]
IfTrue,
#[token("If false")]
IfFalse,
#[token("throw to monkey")]
ThrowToMonkey,
#[regex(r"[\r\n]+")]
NewLine,
#[error]
#[regex(r"[ ]+", logos::skip)]
Error,
}
#[derive(Debug)]
pub enum Error {
UnexpectedToken(Span, Token, Vec<Token>),
MissingToken(Vec<Token>),
}
fn expect(lex: &mut Lexer<Token>, expect: Token) -> Result<Token, Error> {
if let Some(next) = lex.next() {
if next == expect {
Ok(next)
} else {
Err(Error::UnexpectedToken(lex.span(), next, vec![expect]))
}
} else {
Err(Error::MissingToken(vec![expect]))
}
}
impl MonkeyBusiness {
pub fn parse(source: &str) -> Result<Self, Error> {
let mut lex = Token::lexer(source);
let mut monkeys = vec![];
while !lex.remainder().is_empty() {
expect(&mut lex, Token::Monkey)?;
expect(&mut lex, Token::Number(monkeys.len() as WorryLevel))?;
expect(&mut lex, Token::Colon)?;
expect(&mut lex, Token::NewLine)?;
let monkey = Monkey::parse(&mut lex)?;
monkeys.push(monkey);
}
let lcm = monkeys.iter().map(|monkey| monkey.test_div_by).product();
// Presize the holding lists
let total_item_count: usize = monkeys.iter().map(|monkey| monkey.holding.len()).sum();
monkeys.iter_mut().for_each(|monkey| {
monkey
.holding
.reserve(total_item_count - monkey.holding.len())
});
Ok(Self { monkeys, lcm })
}
}
impl Monkey {
fn parse(lex: &mut Lexer<Token>) -> Result<Self, Error> {
expect(lex, Token::StartingItems)?;
expect(lex, Token::Colon)?;
let holding = parse_item_list(lex)?;
expect(lex, Token::Operation)?;
expect(lex, Token::Colon)?;
let operation = Operation::parse(lex)?;
expect(lex, Token::Test)?;
expect(lex, Token::Colon)?;
let test_div_by = parse_test_div_by(lex)?;
expect(lex, Token::IfTrue)?;
expect(lex, Token::Colon)?;
expect(lex, Token::ThrowToMonkey)?;
let on_test_true_target = parse_number(lex)? as usize;
expect(lex, Token::NewLine)?;
expect(lex, Token::IfFalse)?;
expect(lex, Token::Colon)?;
expect(lex, Token::ThrowToMonkey)?;
let on_test_false_target = parse_number(lex)? as usize;
expect(lex, Token::NewLine)?;
Ok(Self {
holding,
operation,
test_div_by,
on_test_true_target,
on_test_false_target,
inspection_count: 0,
})
}
}
fn parse_test_div_by(lex: &mut Lexer<Token>) -> Result<WorryLevel, Error> {
match lex.next() {
Some(Token::DivisibleBy) => {
let number = parse_number(lex)?;
expect(lex, Token::NewLine)?;
Ok(number)
}
Some(token) => Err(Error::UnexpectedToken(
lex.span(),
token,
vec![Token::DivisibleBy],
)),
None => Err(Error::MissingToken(vec![Token::DivisibleBy])),
}
}
impl Operation {
fn parse(lex: &mut Lexer<Token>) -> Result<Self, Error> {
expect(lex, Token::New)?;
expect(lex, Token::EqualSign)?;
expect(lex, Token::Old)?;
match lex.next() {
Some(Token::Multiplication) => {
let op = match lex.next() {
Some(Token::Number(number)) => Ok(Self::Mul(number)),
Some(Token::Old) => Ok(Self::Square),
Some(token) => Err(Error::UnexpectedToken(
lex.span(),
token,
vec![Token::Number(0), Token::Old],
)),
None => Err(Error::MissingToken(vec![Token::Number(0), Token::Old])),
};
expect(lex, Token::NewLine)?;
op
}
Some(Token::Addition) => {
let number = parse_number(lex)?;
expect(lex, Token::NewLine)?;
Ok(Self::Add(number))
}
Some(token) => Err(Error::UnexpectedToken(
lex.span(),
token,
vec![Token::Multiplication, Token::Addition],
)),
None => Err(Error::MissingToken(vec![
Token::Multiplication,
Token::Addition,
])),
}
}
}
fn parse_item_list(lex: &mut Lexer<Token>) -> Result<Vec<WorryLevel>, Error> {
let mut ret = vec![];
loop {
ret.push(parse_number(lex)?);
match lex.next() {
Some(Token::Comma) | None => {}
Some(Token::NewLine) => break,
Some(token) => {
return Err(Error::UnexpectedToken(
lex.span(),
token,
vec![Token::NewLine, Token::Comma],
))
}
}
}
Ok(ret)
}
fn parse_number(lex: &mut Lexer<Token>) -> Result<WorryLevel, Error> {
match lex.next() {
Some(Token::Number(number)) => Ok(number),
Some(token) => Err(Error::UnexpectedToken(
lex.span(),
token,
vec![Token::Number(0)],
)),
None => Err(Error::MissingToken(vec![Token::Number(0)])),
}
}