Compare commits
2 commits
daa8b6b1d0
...
75c7b08449
| Author | SHA1 | Date | |
|---|---|---|---|
| 75c7b08449 | |||
| 6f3e94c5d1 |
11 changed files with 1527 additions and 95 deletions
16
.vscode/launch.json
vendored
16
.vscode/launch.json
vendored
|
|
@ -7,15 +7,15 @@
|
||||||
{
|
{
|
||||||
"type": "lldb",
|
"type": "lldb",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Debug executable 'advent2019'",
|
"name": "Debug executable 'advent2022'",
|
||||||
"cargo": {
|
"cargo": {
|
||||||
"args": [
|
"args": [
|
||||||
"build",
|
"build",
|
||||||
"--bin=advent2019",
|
"--bin=advent2022",
|
||||||
"--package=advent2019"
|
"--package=advent2022"
|
||||||
],
|
],
|
||||||
"filter": {
|
"filter": {
|
||||||
"name": "advent2019",
|
"name": "advent2022",
|
||||||
"kind": "bin"
|
"kind": "bin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -30,16 +30,16 @@
|
||||||
{
|
{
|
||||||
"type": "lldb",
|
"type": "lldb",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Debug unit tests in executable 'advent2019'",
|
"name": "Debug unit tests in executable 'advent2022'",
|
||||||
"cargo": {
|
"cargo": {
|
||||||
"args": [
|
"args": [
|
||||||
"test",
|
"test",
|
||||||
"--no-run",
|
"--no-run",
|
||||||
"--bin=advent2019",
|
"--bin=advent2022",
|
||||||
"--package=advent2019"
|
"--package=advent2022"
|
||||||
],
|
],
|
||||||
"filter": {
|
"filter": {
|
||||||
"name": "advent2019",
|
"name": "advent2022",
|
||||||
"kind": "bin"
|
"kind": "bin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
{
|
{
|
||||||
"editor.formatOnSave": true
|
"editor.formatOnSave": true,
|
||||||
|
"debug.allowBreakpointsEverywhere": true
|
||||||
}
|
}
|
||||||
|
|
@ -8,7 +8,6 @@ edition = "2021"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
const_format = "0.2.31"
|
const_format = "0.2.31"
|
||||||
itertools = "0.11"
|
itertools = "0.11"
|
||||||
lazy_static = "1.4"
|
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
once_cell = "1.18.0"
|
once_cell = "1.18.0"
|
||||||
regex = "1.7"
|
regex = "1.7"
|
||||||
|
|
|
||||||
17
data/day17/blocks.txt
Normal file
17
data/day17/blocks.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
####
|
||||||
|
|
||||||
|
.#.
|
||||||
|
###
|
||||||
|
.#.
|
||||||
|
|
||||||
|
..#
|
||||||
|
..#
|
||||||
|
###
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
##
|
||||||
|
##
|
||||||
1
data/day17/example01.txt
Normal file
1
data/day17/example01.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
>>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>
|
||||||
1
data/day17/input.txt
Normal file
1
data/day17/input.txt
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,5 @@
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use lazy_static::lazy_static;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::{iter::zip, num::ParseIntError};
|
use std::{iter::zip, num::ParseIntError};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -72,13 +72,14 @@ struct Monkey {
|
||||||
bad_monkey: usize,
|
bad_monkey: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
static MONKEY: Lazy<Regex> = Lazy::new(|| Regex::new(r"Monkey (\d+)").unwrap());
|
||||||
static ref MONKEY: Regex = Regex::new(r"Monkey (\d+)").unwrap();
|
static STARTING: Lazy<Regex> =
|
||||||
static ref STARTING: Regex = Regex::new(r"Starting items: (\d+(?:, \d+)*)").unwrap();
|
Lazy::new(|| Regex::new(r"Starting items: (\d+(?:, \d+)*)").unwrap());
|
||||||
static ref OP: Regex = Regex::new(r"Operation: new = old ([+*] \d+|\* old)").unwrap();
|
static OP: Lazy<Regex> =
|
||||||
static ref TEST: Regex = Regex::new(r"Test: divisible by (\d+)").unwrap();
|
Lazy::new(|| Regex::new(r"Operation: new = old ([+*] \d+|\* old)").unwrap());
|
||||||
static ref NEXT: Regex = Regex::new(r"If (?:true|false): throw to monkey (\d+)").unwrap();
|
static TEST: Lazy<Regex> = Lazy::new(|| Regex::new(r"Test: divisible by (\d+)").unwrap());
|
||||||
}
|
static NEXT: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(r"If (?:true|false): throw to monkey (\d+)").unwrap());
|
||||||
|
|
||||||
impl Monkey {
|
impl Monkey {
|
||||||
fn parse_line<'a>(re: &Regex, line: &'a str) -> Result<&'a str, MonkeyError> {
|
fn parse_line<'a>(re: &Regex, line: &'a str) -> Result<&'a str, MonkeyError> {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use crate::common::pos::Pos;
|
use crate::common::pos::Pos;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lazy_static::lazy_static;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::{collections::HashSet, num::ParseIntError};
|
use std::{collections::HashSet, num::ParseIntError};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -209,9 +209,8 @@ struct Sensor {
|
||||||
radius: i64,
|
radius: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
static SENSOR: Lazy<Regex> =
|
||||||
static ref SENSOR: Regex = Regex::new(r"x=(-?\d+), y=(-?\d+).*x=(-?\d+), y=(-?\d+)").unwrap();
|
Lazy::new(|| Regex::new(r"x=(-?\d+), y=(-?\d+).*x=(-?\d+), y=(-?\d+)").unwrap());
|
||||||
}
|
|
||||||
|
|
||||||
impl Sensor {
|
impl Sensor {
|
||||||
pub fn parse(line: &str) -> Result<(Sensor, Pos<i64>), SensorError> {
|
pub fn parse(line: &str) -> Result<(Sensor, Pos<i64>), SensorError> {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
424
src/days/day17/mod.rs
Normal file
424
src/days/day17/mod.rs
Normal file
|
|
@ -0,0 +1,424 @@
|
||||||
|
use std::{cell::Cell, marker::PhantomData, ops::Index};
|
||||||
|
|
||||||
|
use crate::common::file::read_lines;
|
||||||
|
|
||||||
|
use super::template::{DayTrait, ResultType};
|
||||||
|
use itertools::Itertools;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
const DAY_NUMBER: usize = 17;
|
||||||
|
const STACK_WIDTH: usize = 7;
|
||||||
|
|
||||||
|
pub struct Day;
|
||||||
|
|
||||||
|
impl DayTrait for Day {
|
||||||
|
fn get_day_number(&self) -> usize {
|
||||||
|
DAY_NUMBER
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||||
|
let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?);
|
||||||
|
|
||||||
|
let result = Day::run(pushes, 2022)?;
|
||||||
|
|
||||||
|
Ok(ResultType::Integer(result as i64))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||||
|
let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?);
|
||||||
|
|
||||||
|
let result = Day::run(pushes, 1000000000000)?;
|
||||||
|
|
||||||
|
Ok(ResultType::Integer(result as i64))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Day {
|
||||||
|
fn run(push_cycle: Dispenser<Push>, max_cycles: i64) -> Result<i64, RockError> {
|
||||||
|
let raw = read_lines(DAY_NUMBER, "blocks.txt")?;
|
||||||
|
let rock_cycle = Dispenser::new(Rock::parse(&raw)?);
|
||||||
|
let mut cycle = 0;
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
let mut last_drop = None;
|
||||||
|
let mut prev_bottom = 0;
|
||||||
|
let mut additional_height = None;
|
||||||
|
|
||||||
|
while cycle < max_cycles {
|
||||||
|
let bottom = stack.one_rock(rock_cycle.next(), &push_cycle);
|
||||||
|
if bottom < prev_bottom {
|
||||||
|
let drop_distance = prev_bottom - bottom;
|
||||||
|
|
||||||
|
match last_drop {
|
||||||
|
None => {
|
||||||
|
last_drop = Some((bottom, drop_distance, cycle, None));
|
||||||
|
}
|
||||||
|
Some((_, last_distance, _, _)) if last_distance < drop_distance => {
|
||||||
|
last_drop = Some((bottom, drop_distance, cycle, None));
|
||||||
|
}
|
||||||
|
Some((last_bottom, last_distance, last_cycle, None))
|
||||||
|
if last_distance == drop_distance =>
|
||||||
|
{
|
||||||
|
last_drop = Some((
|
||||||
|
bottom,
|
||||||
|
drop_distance,
|
||||||
|
cycle,
|
||||||
|
Some((bottom - last_bottom, cycle - last_cycle)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Some((last_bottom, last_distance, last_cycle, Some((growth, period))))
|
||||||
|
if last_distance == drop_distance =>
|
||||||
|
{
|
||||||
|
if growth == bottom - last_bottom && period == cycle - last_cycle {
|
||||||
|
let first_bottom = last_bottom - growth;
|
||||||
|
let all_equal = (0..growth).all(|row| {
|
||||||
|
stack.0[first_bottom + row] == stack.0[last_bottom + row]
|
||||||
|
});
|
||||||
|
if all_equal {
|
||||||
|
let iterations = (max_cycles - cycle) / period;
|
||||||
|
additional_height = Some(iterations * growth as i64);
|
||||||
|
cycle += iterations * period;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
last_drop = Some((
|
||||||
|
bottom,
|
||||||
|
drop_distance,
|
||||||
|
cycle,
|
||||||
|
Some((bottom - last_bottom, cycle - last_cycle)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_bottom = bottom;
|
||||||
|
cycle += 1;
|
||||||
|
}
|
||||||
|
match additional_height {
|
||||||
|
None => Ok(stack.height() as i64),
|
||||||
|
Some(height) => Ok(stack.height() as i64 + height),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
enum RockError {
|
||||||
|
#[error("IO Error")]
|
||||||
|
RockIOError(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("A rock must not be zero length or hight")]
|
||||||
|
MustNotBeEmpty,
|
||||||
|
|
||||||
|
#[error("All lines in a block must be the same length")]
|
||||||
|
AllLinesSameLength,
|
||||||
|
|
||||||
|
#[error("Unknown direction: {0}")]
|
||||||
|
UnknownDirection(char),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
struct Rock {
|
||||||
|
positions: Vec<(usize, usize)>,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rock {
|
||||||
|
pub fn new(blocks: Vec<Vec<bool>>) -> Result<Rock, RockError> {
|
||||||
|
let height = blocks.len();
|
||||||
|
if height == 0 {
|
||||||
|
return Err(RockError::MustNotBeEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = blocks.iter().map(|line| line.len()).max().unwrap_or(0);
|
||||||
|
if width == 0 {
|
||||||
|
return Err(RockError::MustNotBeEmpty);
|
||||||
|
}
|
||||||
|
for line in blocks.iter() {
|
||||||
|
if line.len() != width {
|
||||||
|
return Err(RockError::AllLinesSameLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let positions = blocks
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(y, line)| {
|
||||||
|
line.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(move |(x, block)| block.then_some((x, height - 1 - y)))
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
Ok(Rock {
|
||||||
|
positions,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(lines: &[String]) -> Result<Vec<Rock>, RockError> {
|
||||||
|
lines
|
||||||
|
.iter()
|
||||||
|
.batching(|it| {
|
||||||
|
let blocks = it
|
||||||
|
.skip_while(|line| line.is_empty())
|
||||||
|
.take_while(|line| !line.is_empty())
|
||||||
|
.map(|line| line.chars().map(|c| c == '#').collect_vec())
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
if blocks.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Rock::new(blocks))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.try_collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum Push {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Push {
|
||||||
|
pub fn parse(c: char) -> Result<Push, RockError> {
|
||||||
|
match c {
|
||||||
|
'<' => Ok(Push::Left),
|
||||||
|
'>' => Ok(Push::Right),
|
||||||
|
_ => Err(RockError::UnknownDirection(c)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FallingRock<'a> {
|
||||||
|
rock: &'a Rock,
|
||||||
|
bottom: usize,
|
||||||
|
left: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FallingRock<'a> {
|
||||||
|
pub fn new(rock: &'a Rock, stack_height: usize) -> FallingRock<'a> {
|
||||||
|
FallingRock {
|
||||||
|
rock,
|
||||||
|
bottom: stack_height + 3,
|
||||||
|
left: 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_push(mut self, push: &Push, stack: &Stack) -> Self {
|
||||||
|
let left = match push {
|
||||||
|
Push::Left => {
|
||||||
|
if self.left == 0 {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
self.left - 1
|
||||||
|
}
|
||||||
|
Push::Right => {
|
||||||
|
if self.left + self.rock.width >= STACK_WIDTH {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
self.left + 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if self.check_position(left, self.bottom, stack) {
|
||||||
|
self.left = left;
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_drop(mut self, stack: &Stack) -> Result<Self, Self> {
|
||||||
|
if self.bottom == 0 {
|
||||||
|
return Err(self);
|
||||||
|
}
|
||||||
|
let bottom = self.bottom - 1;
|
||||||
|
if self.check_position(self.left, bottom, stack) {
|
||||||
|
self.bottom = bottom;
|
||||||
|
Ok(self)
|
||||||
|
} else {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_position(&self, left: usize, bottom: usize, stack: &Stack) -> bool {
|
||||||
|
self.rock
|
||||||
|
.positions
|
||||||
|
.iter()
|
||||||
|
.all(|(x, y)| *y + bottom >= stack.height() || !stack[*y + bottom][*x + left])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reach(&self) -> usize {
|
||||||
|
self.bottom + self.rock.height
|
||||||
|
}
|
||||||
|
|
||||||
|
fn positions(&'a self) -> impl Iterator<Item = (usize, usize)> + 'a {
|
||||||
|
self.rock
|
||||||
|
.positions
|
||||||
|
.iter()
|
||||||
|
.map(|(x, y)| (*x + self.left, *y + self.bottom))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Stack(Vec<[bool; STACK_WIDTH]>);
|
||||||
|
|
||||||
|
impl Stack {
|
||||||
|
pub fn new() -> Stack {
|
||||||
|
Stack(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn settle_rock(&mut self, rock: FallingRock<'_>) {
|
||||||
|
for _ in self.0.len()..rock.reach() {
|
||||||
|
self.0.push([false; STACK_WIDTH]);
|
||||||
|
}
|
||||||
|
for (x, y) in rock.positions() {
|
||||||
|
self.0[y][x] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn one_rock(&mut self, rock: &Rock, push_cycle: &Dispenser<Push>) -> usize {
|
||||||
|
let mut rock = FallingRock::new(rock, self.height());
|
||||||
|
loop {
|
||||||
|
let push = push_cycle.next();
|
||||||
|
rock = rock.try_push(push, &self);
|
||||||
|
match rock.try_drop(&self) {
|
||||||
|
Ok(next_rock) => rock = next_rock,
|
||||||
|
Err(rock) => {
|
||||||
|
let bottom = rock.bottom;
|
||||||
|
self.settle_rock(rock);
|
||||||
|
return bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Stack {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let text = self
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|line| line.iter().map(|b| if *b { "#" } else { " " }).join(""))
|
||||||
|
.join("\n");
|
||||||
|
write!(f, "{}", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<usize> for Stack {
|
||||||
|
type Output = [bool; STACK_WIDTH];
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Dispenser<'a, T>
|
||||||
|
where
|
||||||
|
T: 'a,
|
||||||
|
{
|
||||||
|
data: Vec<T>,
|
||||||
|
current: Cell<usize>,
|
||||||
|
_marker: PhantomData<&'a T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Dispenser<'a, T> {
|
||||||
|
pub fn new(data: Vec<T>) -> Self {
|
||||||
|
Dispenser {
|
||||||
|
data,
|
||||||
|
current: Cell::new(0),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&'a self) -> &'a T {
|
||||||
|
let idx = self.current.get();
|
||||||
|
self.current.set(self.current.get() + 1);
|
||||||
|
if self.current.get() >= self.data.len() {
|
||||||
|
self.current.set(0);
|
||||||
|
}
|
||||||
|
self.data.get(idx).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::common::file::read_lines;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part1() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||||
|
let expected = ResultType::Integer(3068);
|
||||||
|
let result = day.part1(&lines)?;
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part2() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||||
|
let expected = ResultType::Integer(1514285714288);
|
||||||
|
let result = day.part2(&lines)?;
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_blocks() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let raw = read_lines(day.get_day_number(), "blocks.txt")?;
|
||||||
|
let rocks = Rock::parse(&raw)?;
|
||||||
|
let expected = Rock::new(vec![vec![true]; 4])?;
|
||||||
|
assert_eq!(rocks[3], expected);
|
||||||
|
|
||||||
|
let angle_rock = &rocks[2];
|
||||||
|
assert_eq!(
|
||||||
|
angle_rock.positions,
|
||||||
|
vec![(2, 2), (2, 1), (0, 0), (1, 0), (2, 0)]
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn drop_one() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||||
|
let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?);
|
||||||
|
let raw = read_lines(day.get_day_number(), "blocks.txt")?;
|
||||||
|
let rocks = Dispenser::new(Rock::parse(&raw)?);
|
||||||
|
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
stack.one_rock(rocks.next(), &pushes);
|
||||||
|
|
||||||
|
assert_eq!(stack.0, vec![[false, false, true, true, true, true, false]]);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn drop_some() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||||
|
let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?);
|
||||||
|
|
||||||
|
let result = Day::run(pushes, 10)?;
|
||||||
|
|
||||||
|
assert_eq!(result, 17);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@ mod day13;
|
||||||
mod day14;
|
mod day14;
|
||||||
mod day15;
|
mod day15;
|
||||||
mod day16;
|
mod day16;
|
||||||
|
mod day17;
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
pub use template::DayTrait;
|
pub use template::DayTrait;
|
||||||
|
|
@ -23,7 +24,7 @@ pub mod day_provider {
|
||||||
use super::*;
|
use super::*;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
const MAX_DAY: usize = 16;
|
const MAX_DAY: usize = 17;
|
||||||
|
|
||||||
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
||||||
match day_num {
|
match day_num {
|
||||||
|
|
@ -43,6 +44,7 @@ pub mod day_provider {
|
||||||
14 => Ok(Box::new(day14::Day)),
|
14 => Ok(Box::new(day14::Day)),
|
||||||
15 => Ok(Box::new(day15::Day)),
|
15 => Ok(Box::new(day15::Day)),
|
||||||
16 => Ok(Box::new(day16::Day)),
|
16 => Ok(Box::new(day16::Day)),
|
||||||
|
17 => Ok(Box::new(day17::Day)),
|
||||||
_ => Err(ProviderError::InvalidNumber(day_num)),
|
_ => Err(ProviderError::InvalidNumber(day_num)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue