day 20 finished
This commit is contained in:
parent
1972b7285a
commit
4a4117882a
7 changed files with 15358 additions and 1 deletions
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