day14: some silly and not super effective performance optimizations
This commit is contained in:
		
							
								
								
									
										152
									
								
								14/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								14/src/main.rs
									
									
									
									
									
								
							@@ -31,51 +31,57 @@ fn main() {
 | 
				
			|||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
struct Platform {
 | 
					struct Platform {
 | 
				
			||||||
    rows: Vec<Vec<char>>,
 | 
					    rows: Vec<Vec<char>>,
 | 
				
			||||||
 | 
					    width: usize,
 | 
				
			||||||
 | 
					    height: usize,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<T: BufRead> From<Lines<T>> for Platform {
 | 
					impl<T: BufRead> From<Lines<T>> for Platform {
 | 
				
			||||||
    fn from(lines: Lines<T>) -> Self {
 | 
					    fn from(lines: Lines<T>) -> Self {
 | 
				
			||||||
 | 
					        let rows: Vec<Vec<char>> = lines.map(|line| line.unwrap().chars().collect()).collect();
 | 
				
			||||||
 | 
					        let width = rows[0].len();
 | 
				
			||||||
 | 
					        let height = rows.len();
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            rows: lines.map(|line| line.unwrap().chars().collect()).collect(),
 | 
					            rows,
 | 
				
			||||||
 | 
					            width,
 | 
				
			||||||
 | 
					            height,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum Direction {
 | 
					 | 
				
			||||||
    North,
 | 
					 | 
				
			||||||
    South,
 | 
					 | 
				
			||||||
    East,
 | 
					 | 
				
			||||||
    West,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug, PartialEq)]
 | 
					#[derive(Debug, PartialEq)]
 | 
				
			||||||
enum Axis {
 | 
					enum Axis {
 | 
				
			||||||
    Rows,
 | 
					    Rows,
 | 
				
			||||||
    Columns,
 | 
					    Columns,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Direction {
 | 
					struct North;
 | 
				
			||||||
    fn reverse_direction(&self) -> bool {
 | 
					struct South;
 | 
				
			||||||
        match self {
 | 
					struct East;
 | 
				
			||||||
            Direction::North | Direction::West => false,
 | 
					struct West;
 | 
				
			||||||
            Direction::South | Direction::East => true,
 | 
					
 | 
				
			||||||
 | 
					trait Direction {
 | 
				
			||||||
 | 
					    const REVERSED: bool;
 | 
				
			||||||
 | 
					    const AXIS: Axis;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Direction for North {
 | 
				
			||||||
 | 
					    const REVERSED: bool = false;
 | 
				
			||||||
 | 
					    const AXIS: Axis = Axis::Columns;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
    fn axis(&self) -> Axis {
 | 
					impl Direction for South {
 | 
				
			||||||
        match self {
 | 
					    const REVERSED: bool = true;
 | 
				
			||||||
            Direction::North | Direction::South => Axis::Columns,
 | 
					    const AXIS: Axis = Axis::Columns;
 | 
				
			||||||
            Direction::East | Direction::West => Axis::Rows,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					impl Direction for East {
 | 
				
			||||||
 | 
					    const REVERSED: bool = true;
 | 
				
			||||||
 | 
					    const AXIS: Axis = Axis::Rows;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					impl Direction for West {
 | 
				
			||||||
 | 
					    const REVERSED: bool = false;
 | 
				
			||||||
 | 
					    const AXIS: Axis = Axis::Rows;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'a> Platform {
 | 
					impl<'a> Platform {
 | 
				
			||||||
    fn width(&self) -> usize {
 | 
					 | 
				
			||||||
        self.rows[0].len()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    fn height(&self) -> usize {
 | 
					 | 
				
			||||||
        self.col(0).len()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    fn col(&self, col: usize) -> Vec<char> {
 | 
					    fn col(&self, col: usize) -> Vec<char> {
 | 
				
			||||||
        self.rows.iter().map(|row| row[col]).collect()
 | 
					        self.rows.iter().map(|row| row[col]).collect()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -84,97 +90,94 @@ impl<'a> Platform {
 | 
				
			|||||||
            self.rows[row_idx][idx] = col[row_idx];
 | 
					            self.rows[row_idx][idx] = col[row_idx];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fn roll(&mut self, dir: &Direction) {
 | 
					    fn roll<T: Direction>(&mut self) {
 | 
				
			||||||
        match dir.axis() {
 | 
					        match T::AXIS {
 | 
				
			||||||
            Axis::Columns => {
 | 
					            Axis::Columns => {
 | 
				
			||||||
                for idx in 0..self.width() {
 | 
					                for idx in 0..self.width {
 | 
				
			||||||
                    self.roll_one(idx, dir)
 | 
					                    let new = self.roll_one::<T>(idx);
 | 
				
			||||||
 | 
					                    self.replace_col(idx, new);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
            Axis::Rows => {
 | 
					            Axis::Rows => {
 | 
				
			||||||
                for idx in 0..self.height() {
 | 
					                for idx in 0..self.height {
 | 
				
			||||||
                    self.roll_one(idx, dir)
 | 
					                    let new = self.roll_one::<T>(idx);
 | 
				
			||||||
 | 
					                    self.rows[idx].copy_from_slice(new.as_slice());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fn roll_rock_at(old: &mut Vec<char>, inner_idx: usize, dir: &Direction) {
 | 
					    fn roll_rock_at<T: Direction>(old: &mut Vec<char>, inner_idx: usize) {
 | 
				
			||||||
        // Find the first # rock or the edge of the map, we can't look beyond this for a resting position
 | 
					        // Find the first # rock or the edge of the map, we can't look beyond this for a resting position
 | 
				
			||||||
        match dir.reverse_direction() {
 | 
					        if T::REVERSED {
 | 
				
			||||||
            true => {
 | 
					            // The furthest possible resting position is the first # below us, or the edge of the map
 | 
				
			||||||
                let upper_limit = (inner_idx..old.len())
 | 
					            let upper_limit = (inner_idx..old.len() - 1)
 | 
				
			||||||
                .find(|c| old[*c] == '#')
 | 
					                .find(|c| old[*c] == '#')
 | 
				
			||||||
                .unwrap_or(old.len());
 | 
					                .unwrap_or(old.len());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if let Some(empty_pos) = (inner_idx..upper_limit).filter(|i| old[*i] == '.').last()
 | 
					            // We will roll to the position of the furthest '.' before the upper limit
 | 
				
			||||||
                {
 | 
					            if let Some(empty_pos) = (inner_idx..upper_limit).rev().find(|i| old[*i] == '.') {
 | 
				
			||||||
                old.swap(inner_idx, empty_pos);
 | 
					                old.swap(inner_idx, empty_pos);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            }
 | 
					        } else {
 | 
				
			||||||
            false => {
 | 
					            // the furthest possible resting position is the first # above us, or the edge
 | 
				
			||||||
                // Find the first # rock or the edge of the map, we can't look beyond this for a resting position
 | 
					 | 
				
			||||||
            let lower_limit = (0..inner_idx).rev().find(|c| old[*c] == '#').unwrap_or(0);
 | 
					            let lower_limit = (0..inner_idx).rev().find(|c| old[*c] == '#').unwrap_or(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if let Some(empty_pos) = (lower_limit..inner_idx)
 | 
					            // We will roll to the position of the furthest '.' before the lower limit
 | 
				
			||||||
                    .rev()
 | 
					            if let Some(empty_pos) = (lower_limit..inner_idx).find(|i| old[*i] == '.') {
 | 
				
			||||||
                    .filter(|i| old[*i] == '.')
 | 
					 | 
				
			||||||
                    .last()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                old.swap(inner_idx, empty_pos);
 | 
					                old.swap(inner_idx, empty_pos);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    }
 | 
					
 | 
				
			||||||
    fn roll_one(&mut self, idx: usize, dir: &Direction) {
 | 
					    fn roll_one<T: Direction>(&mut self, idx: usize) -> Vec<char> {
 | 
				
			||||||
        let mut old = match dir.axis() {
 | 
					        let mut old = match T::AXIS {
 | 
				
			||||||
            Axis::Columns => self.col(idx),
 | 
					            Axis::Columns => self.col(idx),
 | 
				
			||||||
            Axis::Rows => self.rows[idx].clone(),
 | 
					            Axis::Rows => self.rows[idx].clone(),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let idx_iter: Either<_, _> = match dir.reverse_direction() {
 | 
					        let idx_iter: Either<_, _> = match T::REVERSED {
 | 
				
			||||||
            true => Either::Left((0..old.len()).rev()),
 | 
					            true => Either::Left((0..old.len()).rev()),
 | 
				
			||||||
            false => Either::Right(0..old.len()),
 | 
					            false => Either::Right(0..old.len()),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					        let end_idx = if T::REVERSED { old.len() } else { 0 };
 | 
				
			||||||
        for inner_idx in idx_iter {
 | 
					        for inner_idx in idx_iter {
 | 
				
			||||||
            match old[inner_idx] {
 | 
					            match old[inner_idx] {
 | 
				
			||||||
                '.' | '#' => continue,
 | 
					                '.' | '#' => continue,
 | 
				
			||||||
                'O' if dir.reverse_direction() && inner_idx == old.len() => continue,
 | 
					                'O' if inner_idx == end_idx => continue,
 | 
				
			||||||
                'O' if !dir.reverse_direction() && inner_idx == 0 => continue,
 | 
					                'O' => Self::roll_rock_at::<T>(&mut old, inner_idx),
 | 
				
			||||||
                'O' => Self::roll_rock_at(&mut old, inner_idx, dir),
 | 
					 | 
				
			||||||
                _ => panic!("invalid character"),
 | 
					                _ => panic!("invalid character"),
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        match dir.axis() {
 | 
					        old
 | 
				
			||||||
            Axis::Columns => self.replace_col(idx, old),
 | 
					    }
 | 
				
			||||||
            Axis::Rows => self.rows[idx] = old,
 | 
					    fn score<T: Direction>(&self) -> u64 {
 | 
				
			||||||
 | 
					        match T::AXIS {
 | 
				
			||||||
 | 
					            Axis::Columns => self.score_columns::<T>(),
 | 
				
			||||||
 | 
					            Axis::Rows => self.score_rows::<T>(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fn score(&self, dir: &Direction) -> u64 {
 | 
					    fn row_or_column_score<T: Direction>(&self, idx: usize) -> usize {
 | 
				
			||||||
        match dir.axis() {
 | 
					        if T::REVERSED {
 | 
				
			||||||
            Axis::Columns => self.score_columns(dir),
 | 
					 | 
				
			||||||
            Axis::Rows => self.score_rows(dir),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    fn row_or_column_score(&self, idx: usize, dir: &Direction) -> usize {
 | 
					 | 
				
			||||||
        if dir.reverse_direction() {
 | 
					 | 
				
			||||||
            idx + 1
 | 
					            idx + 1
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            self.rows.len() - idx
 | 
					            match T::AXIS {
 | 
				
			||||||
 | 
					                Axis::Rows => self.width - idx,
 | 
				
			||||||
 | 
					                Axis::Columns => self.height - idx,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    fn score_columns(&self, dir: &Direction) -> u64 {
 | 
					    }
 | 
				
			||||||
        (0..self.height())
 | 
					    fn score_columns<T: Direction>(&self) -> u64 {
 | 
				
			||||||
 | 
					        (0..self.height)
 | 
				
			||||||
            .map(|row| {
 | 
					            .map(|row| {
 | 
				
			||||||
                self.rows[row].iter().filter(|c| **c == 'O').count()
 | 
					                self.rows[row].iter().filter(|c| **c == 'O').count()
 | 
				
			||||||
                    * self.row_or_column_score(row, dir)
 | 
					                    * self.row_or_column_score::<T>(row)
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .sum::<usize>() as u64
 | 
					            .sum::<usize>() as u64
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fn roll_cycle(&mut self) {
 | 
					    fn roll_cycle(&mut self) {
 | 
				
			||||||
        self.roll(&Direction::North);
 | 
					        self.roll::<North>();
 | 
				
			||||||
        self.roll(&Direction::West);
 | 
					        self.roll::<West>();
 | 
				
			||||||
        self.roll(&Direction::South);
 | 
					        self.roll::<South>();
 | 
				
			||||||
        self.roll(&Direction::East);
 | 
					        self.roll::<East>();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // find the first loop, return the iteration count when we first saw it and when we saw it again
 | 
					    // find the first loop, return the iteration count when we first saw it and when we saw it again
 | 
				
			||||||
@@ -190,7 +193,7 @@ impl<'a> Platform {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fn score_rows(&self, dir: &Direction) -> u64 {
 | 
					    fn score_rows<T: Direction>(&self) -> u64 {
 | 
				
			||||||
        unimplemented!()
 | 
					        unimplemented!()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -211,8 +214,8 @@ impl Display for Platform {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
 | 
					fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
 | 
				
			||||||
    let mut p = Platform::from(input);
 | 
					    let mut p = Platform::from(input);
 | 
				
			||||||
    p.roll(&Direction::North);
 | 
					    p.roll::<North>();
 | 
				
			||||||
    p.score(&Direction::North)
 | 
					    p.score::<North>()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PROBLEM 2 solution
 | 
					// PROBLEM 2 solution
 | 
				
			||||||
@@ -223,11 +226,12 @@ fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
 | 
				
			|||||||
    let loop_length = first_loop.1 - first_loop.0;
 | 
					    let loop_length = first_loop.1 - first_loop.0;
 | 
				
			||||||
    let cycles_to_skip = ((ITERATIONS - first_loop.0) / loop_length) * loop_length;
 | 
					    let cycles_to_skip = ((ITERATIONS - first_loop.0) / loop_length) * loop_length;
 | 
				
			||||||
    let iterations_remaining = ITERATIONS - first_loop.0 - cycles_to_skip;
 | 
					    let iterations_remaining = ITERATIONS - first_loop.0 - cycles_to_skip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for _ in 0..iterations_remaining {
 | 
					    for _ in 0..iterations_remaining {
 | 
				
			||||||
        p.roll_cycle();
 | 
					        p.roll_cycle();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    p.score(&Direction::North)
 | 
					    p.score::<North>()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user