Simplify some stuff with clippy suggestions, make Cell struct for each item
This commit is contained in:
parent
560bfe71cc
commit
5c8f3fc2ab
@ -1,33 +1,83 @@
|
|||||||
|
mod node;
|
||||||
|
use node::Node;
|
||||||
|
|
||||||
use aoc_shared::enums::Direction;
|
use aoc_shared::enums::Direction;
|
||||||
use aoc_shared::grid::Grid as BaseGrid;
|
use aoc_shared::grid::Grid as BaseGrid;
|
||||||
use aoc_shared::impl_grid_newtype;
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Grid<T>(BaseGrid<T>);
|
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 {
|
pub fn from_file_str(file_str: &str) -> Self {
|
||||||
let lines: Vec<&str> = file_str.lines().collect();
|
let first_line = file_str.lines().next().unwrap();
|
||||||
let width = lines[0].len();
|
let width = first_line.len();
|
||||||
|
|
||||||
let mut grid = Grid::new(width);
|
let mut grid = Grid::new(width);
|
||||||
lines
|
let mut idx = 0usize;
|
||||||
.into_iter()
|
|
||||||
.map(|line| line.chars())
|
for c in file_str.chars() {
|
||||||
.for_each(|line_chars| grid.vec.append(&mut line_chars.collect::<Vec<char>>()));
|
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
|
grid
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_pos(&self, value: char) -> Option<usize> {
|
fn find_pos(&self, value: CellType) -> Option<usize> {
|
||||||
self.vec.iter().position(|item| *item == value)
|
self.vec.iter().position(|item| item.kind == value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print(&self) {
|
pub fn print(&self) {
|
||||||
for r in 0usize..self.num_rows() {
|
for r in 0usize..self.num_rows() {
|
||||||
let range = self.row_first_idx(r)..=self.row_last_idx(r);
|
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);
|
println!("{}", line);
|
||||||
}
|
}
|
||||||
@ -78,35 +128,20 @@ impl Grid<char> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the character a lowercase letter?
|
let start_char = start_char.unwrap();
|
||||||
let start_char = *start_char.unwrap();
|
let end_char = end_char.unwrap();
|
||||||
let end_char = *end_char.unwrap();
|
let start_elevation = start_char.get_height();
|
||||||
if !(start_char.is_ascii_lowercase() && end_char.is_ascii_lowercase()) {
|
let end_elevation = end_char.get_height();
|
||||||
|
if (end_elevation < start_elevation) || end_elevation.abs_diff(start_elevation) > 1 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the elevation change 0 or 1?
|
let ((start_x, start_y), (end_x, end_y)) = (start_char.coord, end_char.coord);
|
||||||
let start_char = start_char
|
let x_diff = end_x.abs_diff(start_x);
|
||||||
.to_digit(36)
|
let y_diff = end_y.abs_diff(start_y);
|
||||||
.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);
|
|
||||||
|
|
||||||
// Have we moved 0 or 1 in a cardinal direction?
|
// Have we moved 0 or 1 in a cardinal direction?
|
||||||
match (x_diff, y_diff) {
|
matches!((x_diff, y_diff), (0, 0) | (0, 1) | (1, 0))
|
||||||
(0, 0) | (0, 1) | (1, 0) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_moves(&self, start: usize) -> Vec<usize> {
|
fn find_moves(&self, start: usize) -> Vec<usize> {
|
||||||
@ -116,11 +151,9 @@ impl Grid<char> {
|
|||||||
Direction::Left,
|
Direction::Left,
|
||||||
Direction::Right,
|
Direction::Right,
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|d| self.get_index_for_move(start, d))
|
.filter_map(|d| self.get_index_for_move(start, d))
|
||||||
.filter(|m| m.is_some())
|
.collect()
|
||||||
.map(|m| m.unwrap())
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_valid_moves(&self, start: usize) -> Vec<usize> {
|
fn find_valid_moves(&self, start: usize) -> Vec<usize> {
|
||||||
@ -131,117 +164,7 @@ impl Grid<char> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn has_valid_neighbor(&self, idx: usize) -> bool {
|
fn has_valid_neighbor(&self, idx: usize) -> bool {
|
||||||
self.find_valid_moves(idx).len() > 0
|
!self.find_valid_moves(idx).is_empty()
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +174,7 @@ impl Node {
|
|||||||
pub struct Pathfinder {
|
pub struct Pathfinder {
|
||||||
start_idx: usize,
|
start_idx: usize,
|
||||||
end_idx: usize,
|
end_idx: usize,
|
||||||
grid: Grid<char>,
|
grid: Grid<Cell>,
|
||||||
tree: Node,
|
tree: Node,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,28 +186,21 @@ impl Pathfinder {
|
|||||||
grid: Grid::from_file_str(file_str),
|
grid: Grid::from_file_str(file_str),
|
||||||
tree: Node::default(),
|
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
|
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) {
|
fn add_children(&mut self, node: &mut Node, idx: usize) {
|
||||||
let possible_moves = self.grid.find_valid_moves(idx);
|
let possible_moves = self.grid.find_valid_moves(idx);
|
||||||
|
|
||||||
if possible_moves.len() == 0 {
|
if possible_moves.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,9 +209,9 @@ impl Pathfinder {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut n = node.add_child(m);
|
let n = node.add_child(m);
|
||||||
if m != self.end_idx {
|
if m != self.end_idx {
|
||||||
self.add_children(&mut n, m);
|
self.add_children(n, m);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -310,7 +226,7 @@ impl Pathfinder {
|
|||||||
self.tree = tree;
|
self.tree = tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_paths(&self) -> impl Iterator<Item= &Node> {
|
fn get_paths(&self) -> impl Iterator<Item = &Node> {
|
||||||
self.tree
|
self.tree
|
||||||
.get_leaves()
|
.get_leaves()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
Reference in New Issue
Block a user