Simplify some stuff with clippy suggestions, make Cell struct for each item

This commit is contained in:
Timothy Warren 2022-12-23 10:55:25 -05:00
parent 560bfe71cc
commit 5c8f3fc2ab
1 changed files with 86 additions and 170 deletions

View File

@ -1,33 +1,83 @@
mod node;
use node::Node;
use aoc_shared::enums::Direction;
use aoc_shared::grid::Grid as BaseGrid;
use aoc_shared::impl_grid_newtype;
#[derive(Debug, Copy, Clone, PartialEq)]
enum CellType {
Start,
End,
Waypoint(u8),
}
#[derive(Debug, Clone)]
struct Cell {
kind: CellType,
idx: usize,
coord: (usize, usize),
}
impl Cell {
pub fn new(kind: CellType, idx: usize, coord: (usize, usize)) -> Self {
Cell { kind, idx, coord }
}
pub fn get_height(&self) -> u8 {
match self.kind {
CellType::Start => 0,
CellType::End => 25,
CellType::Waypoint(c) => c,
}
}
}
// ----------------------------------------------------------------------------
#[derive(Debug)]
pub struct Grid<T>(BaseGrid<T>);
impl_grid_newtype!(Grid, BaseGrid<char>, char);
impl_grid_newtype!(Grid, BaseGrid<Cell>, Cell);
impl Grid<char> {
impl Grid<Cell> {
pub fn from_file_str(file_str: &str) -> Self {
let lines: Vec<&str> = file_str.lines().collect();
let width = lines[0].len();
let first_line = file_str.lines().next().unwrap();
let width = first_line.len();
let mut grid = Grid::new(width);
lines
.into_iter()
.map(|line| line.chars())
.for_each(|line_chars| grid.vec.append(&mut line_chars.collect::<Vec<char>>()));
let mut idx = 0usize;
for c in file_str.chars() {
let kind = match c {
'S' => CellType::Start,
'E' => CellType::End,
'a'..='z' => CellType::Waypoint(c as u8 - b'a'),
'\r' | '\n' => continue,
_ => panic!("Invalid character: {c}"),
};
let (x, y) = (idx % width, idx / width);
let cell = Cell::new(kind, idx, (x, y));
grid.vec.push(cell);
idx += 1;
}
grid
}
fn find_pos(&self, value: char) -> Option<usize> {
self.vec.iter().position(|item| *item == value)
fn find_pos(&self, value: CellType) -> Option<usize> {
self.vec.iter().position(|item| item.kind == value)
}
pub fn print(&self) {
for r in 0usize..self.num_rows() {
let range = self.row_first_idx(r)..=self.row_last_idx(r);
let line: String = self.vec[range].iter().collect();
let line: String = self.vec[range]
.iter()
.map(|n| (n.get_height() + b'a') as char)
.collect();
println!("{}", line);
}
@ -78,35 +128,20 @@ impl Grid<char> {
return false;
}
// Is the character a lowercase letter?
let start_char = *start_char.unwrap();
let end_char = *end_char.unwrap();
if !(start_char.is_ascii_lowercase() && end_char.is_ascii_lowercase()) {
let start_char = start_char.unwrap();
let end_char = end_char.unwrap();
let start_elevation = start_char.get_height();
let end_elevation = end_char.get_height();
if (end_elevation < start_elevation) || end_elevation.abs_diff(start_elevation) > 1 {
return false;
}
// Is the elevation change 0 or 1?
let start_char = start_char
.to_digit(36)
.expect(&format!("Should be a digit: {}", start_char));
let end_char = end_char
.to_digit(36)
.expect(&format!("Should be a digit: {}", end_char));
let diff = u32::abs_diff(end_char, start_char);
if diff > 1 {
return false;
}
let (start_x, start_y) = self.idx_xy(start);
let (end_x, end_y) = self.idx_xy(end);
let x_diff = usize::abs_diff(end_x, start_x);
let y_diff = usize::abs_diff(end_y, start_y);
let ((start_x, start_y), (end_x, end_y)) = (start_char.coord, end_char.coord);
let x_diff = end_x.abs_diff(start_x);
let y_diff = end_y.abs_diff(start_y);
// Have we moved 0 or 1 in a cardinal direction?
match (x_diff, y_diff) {
(0, 0) | (0, 1) | (1, 0) => true,
_ => false,
}
matches!((x_diff, y_diff), (0, 0) | (0, 1) | (1, 0))
}
fn find_moves(&self, start: usize) -> Vec<usize> {
@ -116,11 +151,9 @@ impl Grid<char> {
Direction::Left,
Direction::Right,
]
.into_iter()
.map(|d| self.get_index_for_move(start, d))
.filter(|m| m.is_some())
.map(|m| m.unwrap())
.collect()
.into_iter()
.filter_map(|d| self.get_index_for_move(start, d))
.collect()
}
fn find_valid_moves(&self, start: usize) -> Vec<usize> {
@ -131,117 +164,7 @@ impl Grid<char> {
}
fn has_valid_neighbor(&self, idx: usize) -> bool {
self.find_valid_moves(idx).len() > 0
}
fn filter_invalid(&mut self, from: usize) {
let (ch, col_indexes, row_indexes) = {
let ch = self.get(from).unwrap();
let (col, row) = self.idx_xy(from);
let col_indexes = self.get_column_indexes(col);
let row_indexes = self.get_row_indexes(row);
(ch, col_indexes, row_indexes)
};
self.vec = self.vec
.clone()
.into_iter()
.enumerate()
.map(|(idx, c)| {
if c != *ch {
return c;
}
return if self.has_valid_neighbor(idx) && (col_indexes.contains(&idx) || row_indexes.contains(&idx)) {
c
} else {
'0'
}
})
.collect();
}
}
// ----------------------------------------------------------------------------
#[derive(Debug, Default, Clone)]
pub struct Node {
idx: usize,
parents: Vec<usize>,
children: Option<Vec<Box<Node>>>,
}
impl Node {
pub fn new(idx: usize) -> Self {
Node {
idx,
..Node::default()
}
}
pub fn add_child(&mut self, value: usize) -> &mut Self {
let mut child = Node::new(value);
child.parents.append(&mut self.parents.clone());
child.parents.push(self.idx);
self.append(child);
self.children
.as_mut()
.expect("There should be a Vec here!")
.last_mut()
.expect("There should be a Box here!")
}
fn append(&mut self, node: Node) -> &mut Self {
match &mut self.children {
Some(c) => {
c.push(Box::new(node));
}
None => self.children = Some(vec![Box::new(node)]),
};
self
}
fn is_leaf(&self) -> bool {
self.children.is_none()
}
pub fn contains(&self, value: usize) -> bool {
if self.idx == value {
return true;
}
return self.parents.contains(&value);
}
pub fn get_leaves(&self) -> Vec<&Node> {
if self.is_leaf() {
return vec![self];
}
let mut leaves = Vec::new();
let children = self
.children
.as_ref()
.unwrap()
.iter()
.map(|boxed| boxed.as_ref());
for child in children {
let mut child_leaves = child.get_leaves();
leaves.append(&mut child_leaves);
}
leaves
}
pub fn get_len(&self) -> usize {
self.parents.len()
!self.find_valid_moves(idx).is_empty()
}
}
@ -251,7 +174,7 @@ impl Node {
pub struct Pathfinder {
start_idx: usize,
end_idx: usize,
grid: Grid<char>,
grid: Grid<Cell>,
tree: Node,
}
@ -263,28 +186,21 @@ impl Pathfinder {
grid: Grid::from_file_str(file_str),
tree: Node::default(),
};
pf.go_to_start();
let start = pf.grid.find_pos(CellType::Start).unwrap();
let end = pf.grid.find_pos(CellType::End).unwrap();
pf.start_idx = start;
pf.end_idx = end;
pf.tree.idx = start;
pf
}
fn go_to_start(&mut self) {
let start = self.grid.find_pos('S').unwrap();
let end = self.grid.find_pos('E').unwrap();
self.start_idx = start;
self.end_idx = end;
self.grid.vec[start] = 'a';
self.grid.vec[end] = 'z';
// self.grid.filter_invalid(start);
self.tree.idx = start;
}
fn add_children(&mut self, node: &mut Node, idx: usize) {
let possible_moves = self.grid.find_valid_moves(idx);
if possible_moves.len() == 0 {
if possible_moves.is_empty() {
return;
}
@ -293,9 +209,9 @@ impl Pathfinder {
continue;
}
let mut n = node.add_child(m);
let n = node.add_child(m);
if m != self.end_idx {
self.add_children(&mut n, m);
self.add_children(n, m);
} else {
break;
}
@ -310,7 +226,7 @@ impl Pathfinder {
self.tree = tree;
}
fn get_paths(&self) -> impl Iterator<Item= &Node> {
fn get_paths(&self) -> impl Iterator<Item = &Node> {
self.tree
.get_leaves()
.into_iter()