day 20 finished

This commit is contained in:
Rüdiger Ludwig 2023-08-05 14:26:29 +02:00
parent 1972b7285a
commit 4a4117882a
7 changed files with 15358 additions and 1 deletions

340
src/days/day20/mod.rs Normal file
View 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(())
}
}

View file

@ -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)),
}
}

View file

@ -1,3 +1,5 @@
#![feature(get_many_mut)]
mod common;
mod days;
mod macros;