Compare commits
2 commits
062ede1df1
...
4a4117882a
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a4117882a | |||
| 1972b7285a |
8 changed files with 15358 additions and 10 deletions
7
data/day20/example01.txt
Normal file
7
data/day20/example01.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
1
|
||||
2
|
||||
-3
|
||||
3
|
||||
-2
|
||||
0
|
||||
4
|
||||
5000
data/day20/input.txt
Normal file
5000
data/day20/input.txt
Normal file
File diff suppressed because it is too large
Load diff
5001
result.txt
Normal file
5001
result.txt
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -78,15 +78,6 @@ enum Material {
|
|||
}
|
||||
|
||||
impl Material {
|
||||
pub fn prev(&self) -> Option<Material> {
|
||||
match self {
|
||||
Material::Geode => None,
|
||||
Material::Obsidian => Some(Material::Geode),
|
||||
Material::Clay => Some(Material::Obsidian),
|
||||
Material::Ore => Some(Material::Clay),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next(&self) -> Option<Material> {
|
||||
match self {
|
||||
Material::Geode => Some(Material::Obsidian),
|
||||
|
|
|
|||
340
src/days/day20/mod.rs
Normal file
340
src/days/day20/mod.rs
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::file::split_lines;
|
||||
use itertools::Itertools;
|
||||
use std::{num::ParseIntError, str::FromStr};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 20;
|
||||
|
||||
pub struct Day;
|
||||
|
||||
impl DayTrait for Day {
|
||||
fn get_day_number(&self) -> usize {
|
||||
DAY_NUMBER
|
||||
}
|
||||
|
||||
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||
let mut ring: Ring = lines.parse()?;
|
||||
ring.mix(1);
|
||||
let start = ring.index_of(0)?;
|
||||
Ok(ResultType::Integer(
|
||||
ring.get_item(start, 1000) + ring.get_item(start, 2000) + ring.get_item(start, 3000),
|
||||
))
|
||||
}
|
||||
|
||||
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||
let mut ring: Ring = lines.parse()?;
|
||||
ring.multiply(811589153);
|
||||
ring.mix(10);
|
||||
let start = ring.index_of(0)?;
|
||||
Ok(ResultType::Integer(
|
||||
ring.get_item(start, 1000) + ring.get_item(start, 2000) + ring.get_item(start, 3000),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum RingError {
|
||||
#[error("Parse Error")]
|
||||
ParseError(#[from] ParseIntError),
|
||||
#[error("Item not found {0}")]
|
||||
ItemNotFound(i64),
|
||||
}
|
||||
|
||||
struct Ring {
|
||||
items: Vec<i64>,
|
||||
next: Vec<usize>,
|
||||
prev: Vec<usize>,
|
||||
}
|
||||
|
||||
impl FromStr for Ring {
|
||||
type Err = RingError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let items: Vec<_> = split_lines(s).map(|line| line.parse()).try_collect()?;
|
||||
Ok(Ring::new(items))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ring {
|
||||
pub fn new(items: Vec<i64>) -> Self {
|
||||
let mut next = Vec::with_capacity(items.len());
|
||||
for i in 1..items.len() {
|
||||
next.push(i);
|
||||
}
|
||||
next.push(0);
|
||||
let mut prev = Vec::with_capacity(items.len());
|
||||
prev.push(items.len() - 1);
|
||||
for i in 0..items.len() - 1 {
|
||||
prev.push(i);
|
||||
}
|
||||
Self { items, next, prev }
|
||||
}
|
||||
|
||||
pub fn get_item(&self, start: usize, steps: usize) -> i64 {
|
||||
let steps = steps % self.items.len();
|
||||
let mut next = start;
|
||||
for _ in 0..steps {
|
||||
next = self.next[next];
|
||||
}
|
||||
self.items[next]
|
||||
}
|
||||
|
||||
pub fn index_of(&self, value: i64) -> Result<usize, RingError> {
|
||||
self.items
|
||||
.iter()
|
||||
.position(|v| *v == value)
|
||||
.ok_or_else(|| RingError::ItemNotFound(value))
|
||||
}
|
||||
|
||||
fn next_pos(&self, pos: usize) -> usize {
|
||||
let len = self.items.len() as i64;
|
||||
let mut moves = self.items[pos] % (len - 1);
|
||||
if moves > len / 2 {
|
||||
moves -= len - 1;
|
||||
} else if moves < -len / 2 {
|
||||
moves += len - 1;
|
||||
}
|
||||
|
||||
match moves.cmp(&0) {
|
||||
std::cmp::Ordering::Equal => pos,
|
||||
std::cmp::Ordering::Less => {
|
||||
let mut prev = self.prev[pos];
|
||||
for _ in moves..-1 {
|
||||
prev = self.prev[prev];
|
||||
}
|
||||
prev
|
||||
}
|
||||
std::cmp::Ordering::Greater => {
|
||||
let mut next = self.next[pos];
|
||||
for _ in 0..moves {
|
||||
next = self.next[next];
|
||||
}
|
||||
next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_item(&mut self, pos: usize) {
|
||||
let next_pos = self.next_pos(pos);
|
||||
if next_pos == pos {
|
||||
return;
|
||||
}
|
||||
let old_next = self.next[pos];
|
||||
self.next[self.prev[pos]] = self.next[pos];
|
||||
self.next[pos] = self.next[self.prev[next_pos]];
|
||||
self.next[self.prev[next_pos]] = pos;
|
||||
|
||||
self.prev[old_next] = self.prev[pos];
|
||||
self.prev[pos] = self.prev[next_pos];
|
||||
self.prev[next_pos] = pos;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn iter(&self) -> RingIter<'_> {
|
||||
let start = self.index_of(0).unwrap_or(0);
|
||||
RingIter::new(self, start)
|
||||
}
|
||||
|
||||
fn mix(&mut self, times: usize) {
|
||||
for _ in 0..times {
|
||||
for pos in 0..self.items.len() {
|
||||
self.move_item(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn multiply(&mut self, arg: i64) {
|
||||
self.items
|
||||
.iter_mut()
|
||||
.for_each(|value| *value = *value * arg);
|
||||
}
|
||||
}
|
||||
|
||||
struct RingIter<'a> {
|
||||
ring: &'a Ring,
|
||||
pos: Option<usize>,
|
||||
start: usize,
|
||||
}
|
||||
|
||||
impl<'a> RingIter<'a> {
|
||||
pub fn new(ring: &'a Ring, start: usize) -> Self {
|
||||
Self {
|
||||
ring,
|
||||
pos: Some(start),
|
||||
start,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for RingIter<'_> {
|
||||
type Item = i64;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let Some(pos) = self.pos else {
|
||||
return None;
|
||||
};
|
||||
let next_pos = self.ring.next[pos];
|
||||
self.pos = if next_pos != self.start {
|
||||
Some(next_pos)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(self.ring.items[pos])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::common::file::read_string;
|
||||
use anyhow::Result;
|
||||
|
||||
#[test]
|
||||
fn test_part1() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::Integer(3);
|
||||
let result = day.part1(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::Integer(1623178306);
|
||||
let result = day.part2(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move1() {
|
||||
let input = vec![1, 2, 3, 4, 5, 6, 7];
|
||||
let mut ring = Ring::new(input);
|
||||
let expected = vec![1, 3, 4, 5, 6, 7, 2];
|
||||
ring.move_item(0);
|
||||
assert_eq!(ring.iter().collect_vec(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move7() {
|
||||
let input = vec![7, 2, 3, 4, 5, 6, 7];
|
||||
let mut ring = Ring::new(input);
|
||||
let expected = vec![7, 3, 4, 5, 6, 7, 2];
|
||||
ring.move_item(0);
|
||||
assert_eq!(ring.iter().collect_vec(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move2() {
|
||||
let input = vec![1, 2, 3, 4, 5, 6, 7];
|
||||
let mut ring = Ring::new(input);
|
||||
let expected = vec![1, 3, 4, 2, 5, 6, 7];
|
||||
ring.move_item(1);
|
||||
assert_eq!(ring.iter().collect_vec(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_6() {
|
||||
let input = vec![6, -2, 5, 6, 7, 8, 9];
|
||||
let mut ring = Ring::new(input);
|
||||
let expected = vec![6, -2, 5, 6, 7, 8, 9];
|
||||
ring.move_item(0);
|
||||
assert_eq!(ring.iter().collect_vec(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_minus1() {
|
||||
let input = vec![4, -1, 5, 6, 7, 8, 9];
|
||||
let mut ring = Ring::new(input);
|
||||
let expected = vec![4, 5, 6, 7, 8, 9, -1];
|
||||
ring.move_item(1);
|
||||
assert_eq!(ring.iter().collect_vec(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_minus2() {
|
||||
let input = vec![4, -2, 5, 6, 7, 8, 9];
|
||||
let mut ring = Ring::new(input);
|
||||
let expected = vec![4, 5, 6, 7, 8, -2, 9];
|
||||
ring.move_item(1);
|
||||
assert_eq!(ring.iter().collect_vec(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_minus4() {
|
||||
let input = vec![4, -4, 5, 6, 7, 8, 9];
|
||||
let mut ring = Ring::new(input);
|
||||
let expected = vec![4, 5, 6, -4, 7, 8, 9];
|
||||
ring.move_item(1);
|
||||
assert_eq!(ring.iter().collect_vec(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mix() {
|
||||
let input = vec![1, 2, -3, 3, -2, 0, 4];
|
||||
let mut ring = Ring::new(input);
|
||||
let expected = vec![0, 3, -2, 1, 2, -3, 4];
|
||||
ring.mix(1);
|
||||
assert_eq!(ring.iter().collect_vec(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_item() {
|
||||
let input = vec![1, 2, 3, 4, 5, 6, 7];
|
||||
let ring = Ring::new(input);
|
||||
assert_eq!(ring.get_item(0, 0), 1);
|
||||
assert_eq!(ring.get_item(0, 6), 7);
|
||||
assert_eq!(ring.get_item(0, 7), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_part1() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||
let mut ring: Ring = lines.parse()?;
|
||||
ring.mix(1);
|
||||
let expected = vec![0, 3, -2, 1, 2, -3, 4];
|
||||
assert_eq!(ring.iter().collect_vec(), expected);
|
||||
|
||||
let start = ring.index_of(0)?;
|
||||
assert_eq!(ring.get_item(start, 0), 0);
|
||||
assert_eq!(ring.get_item(start, 1000), 4);
|
||||
assert_eq!(ring.get_item(start, 2000), -3);
|
||||
assert_eq!(ring.get_item(start, 3000), 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_part2() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||
let mut ring: Ring = lines.parse()?;
|
||||
ring.multiply(811589153);
|
||||
ring.mix(10);
|
||||
let expected = vec![
|
||||
0,
|
||||
-2434767459,
|
||||
1623178306,
|
||||
3246356612,
|
||||
-1623178306,
|
||||
2434767459,
|
||||
811589153,
|
||||
];
|
||||
assert_eq!(ring.iter().collect_vec(), expected);
|
||||
|
||||
let start = ring.index_of(0)?;
|
||||
assert_eq!(ring.get_item(start, 0), 0);
|
||||
assert_eq!(ring.get_item(start, 1000), 811589153);
|
||||
assert_eq!(ring.get_item(start, 2000), 2434767459);
|
||||
assert_eq!(ring.get_item(start, 3000), -1623178306);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ mod day16;
|
|||
mod day17;
|
||||
mod day18;
|
||||
mod day19;
|
||||
mod day20;
|
||||
mod template;
|
||||
|
||||
pub use template::DayTrait;
|
||||
|
|
@ -26,7 +27,7 @@ pub mod day_provider {
|
|||
use super::*;
|
||||
use thiserror::Error;
|
||||
|
||||
const MAX_DAY: usize = 19;
|
||||
const MAX_DAY: usize = 20;
|
||||
|
||||
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
||||
match day_num {
|
||||
|
|
@ -49,6 +50,7 @@ pub mod day_provider {
|
|||
17 => Ok(Box::new(day17::Day)),
|
||||
18 => Ok(Box::new(day18::Day)),
|
||||
19 => Ok(Box::new(day19::Day)),
|
||||
20 => Ok(Box::new(day20::Day)),
|
||||
_ => Err(ProviderError::InvalidNumber(day_num)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#![feature(get_many_mut)]
|
||||
|
||||
mod common;
|
||||
mod days;
|
||||
mod macros;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue