Day 10&11
This commit is contained in:
parent
3eaea9577a
commit
afc1255c76
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
/target
|
||||
result
|
||||
result*
|
||||
flamegraph.svg
|
||||
.direnv
|
||||
perf.data*
|
||||
|
|
44
Cargo.lock
generated
44
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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
137
day10/input
Normal 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
146
day10/input.test
Normal 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
|
|
@ -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
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,3 +8,4 @@ build = true
|
|||
app = true
|
||||
|
||||
[dependencies]
|
||||
logos = "0.12.1"
|
||||
|
|
55
day11/input
Normal file
55
day11/input
Normal 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
27
day11/input.test
Normal 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
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
80
day11/src/monkey_business/mod.rs
Normal file
80
day11/src/monkey_business/mod.rs
Normal 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
|
||||
}
|
||||
}
|
205
day11/src/monkey_business/parser.rs
Normal file
205
day11/src/monkey_business/parser.rs
Normal 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)])),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue