Most of the implementation for 2023 Day 3 Part 1
This commit is contained in:
parent
34c31b75da
commit
161b80fae0
10
2023/day3/src/example_input.txt
Normal file
10
2023/day3/src/example_input.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
467..114..
|
||||||
|
...*......
|
||||||
|
..35..633.
|
||||||
|
......#...
|
||||||
|
617*......
|
||||||
|
.....+.58.
|
||||||
|
..592.....
|
||||||
|
......755.
|
||||||
|
...$.*....
|
||||||
|
.664.598..
|
@ -1,10 +1,223 @@
|
|||||||
|
use crate::ValueType::Number;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
const FILE_STR: &'static str = include_str!("input.txt");
|
const FILE_STR: &'static str = include_str!("input.txt");
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct NumberLocation {
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
|
digits: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NumberLocation {
|
||||||
|
fn new(start: usize, value: char) -> Self {
|
||||||
|
NumberLocation {
|
||||||
|
start,
|
||||||
|
end: start + 1,
|
||||||
|
digits: String::from(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_digit(&mut self, digit: char) {
|
||||||
|
self.end += 1;
|
||||||
|
self.digits.push(digit);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_number(&self) -> usize {
|
||||||
|
self.digits.parse::<usize>().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq, Hash)]
|
||||||
|
enum ValueType {
|
||||||
|
Number(usize),
|
||||||
|
Symbol,
|
||||||
|
Empty,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Grid {
|
||||||
|
line_size: usize,
|
||||||
|
numbers: Vec<NumberLocation>,
|
||||||
|
values: Vec<ValueType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Grid {
|
||||||
|
pub fn parse(input_str: &str) -> Self {
|
||||||
|
let len = input_str.split('\n').next().unwrap().len();
|
||||||
|
let mut values: Vec<ValueType> = Vec::new();
|
||||||
|
let mut numbers: Vec<NumberLocation> = Vec::new();
|
||||||
|
let chars: Vec<char> = input_str.replace("\n", "").chars().collect();
|
||||||
|
let mut prev = ValueType::Empty;
|
||||||
|
chars.iter().enumerate().for_each(|(i, ch)| {
|
||||||
|
if ch.is_digit(10) {
|
||||||
|
if let Number(_) = prev {
|
||||||
|
let final_num = { numbers.len() - 1 };
|
||||||
|
numbers.get_mut(final_num).unwrap().add_digit(*ch);
|
||||||
|
} else {
|
||||||
|
let nl = NumberLocation::new(i, *ch);
|
||||||
|
numbers.push(nl);
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = Number(numbers.len() - 1);
|
||||||
|
} else if *ch == '.' {
|
||||||
|
prev = ValueType::Empty;
|
||||||
|
} else {
|
||||||
|
prev = ValueType::Symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
values.push(prev);
|
||||||
|
});
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
line_size: len,
|
||||||
|
numbers,
|
||||||
|
values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn num_cols(&self) -> usize {
|
||||||
|
self.line_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn num_rows(&self) -> usize {
|
||||||
|
self.values.len() / self.num_cols()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert x,y coordinate into linear array index
|
||||||
|
pub fn xy_idx(&self, x: usize, y: usize) -> usize {
|
||||||
|
(y * self.num_cols()) + x
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert linear array index to x,y coordinate
|
||||||
|
pub fn idx_xy(&self, idx: usize) -> (usize, usize) {
|
||||||
|
(idx % self.num_cols(), idx / self.num_cols())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjacent_ind(&self, i: usize) -> HashSet<usize> {
|
||||||
|
let mut ind = HashSet::new();
|
||||||
|
if i >= self.values.len() {
|
||||||
|
return ind;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forwards/backwards
|
||||||
|
if i > 0 {
|
||||||
|
ind.insert(i - 1);
|
||||||
|
}
|
||||||
|
if i + 1 < self.values.len() {
|
||||||
|
ind.insert(i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row above/below
|
||||||
|
let (col, row) = self.idx_xy(i);
|
||||||
|
if row + 1 < self.num_rows() {
|
||||||
|
ind.insert(self.xy_idx(col, row + 1));
|
||||||
|
}
|
||||||
|
if row > 0 {
|
||||||
|
ind.insert(self.xy_idx(col, row - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diagonals (x+1, y-1),(x-1, y-1),(x+1, y+1),(x-1,y+1)
|
||||||
|
if col > 0 && row > 0 {
|
||||||
|
ind.insert(self.xy_idx(col - 1, row - 1));
|
||||||
|
}
|
||||||
|
if col > 0 && row + 1 < self.num_rows() {
|
||||||
|
ind.insert(self.xy_idx(col - 1, row + 1));
|
||||||
|
}
|
||||||
|
if col + 1 < self.num_cols() && row > 0 {
|
||||||
|
ind.insert(self.xy_idx(col + 1, row - 1));
|
||||||
|
}
|
||||||
|
if col + 1 < self.num_cols() && row + 1 < self.num_rows() {
|
||||||
|
ind.insert(self.xy_idx(col + 1, row + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
ind
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_adjacent(&self, r: Range<usize>) -> Vec<ValueType> {
|
||||||
|
let mut adj = Vec::new();
|
||||||
|
|
||||||
|
for i in r {
|
||||||
|
self.adjacent_ind(i)
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|ind| adj.push(self.values[ind]));
|
||||||
|
}
|
||||||
|
|
||||||
|
adj
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_part_number(&self, r: Range<usize>) -> bool {
|
||||||
|
self.find_adjacent(r).contains(&ValueType::Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_part_numbers(&self) -> Vec<usize> {
|
||||||
|
self.numbers
|
||||||
|
.iter()
|
||||||
|
.filter(|nl| {
|
||||||
|
let range = nl.start..nl.end;
|
||||||
|
self.is_part_number(range)
|
||||||
|
})
|
||||||
|
.map(|nl| nl.to_number())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_part_number_sum(&self) -> usize {
|
||||||
|
self.numbers
|
||||||
|
.iter()
|
||||||
|
.filter(|nl| {
|
||||||
|
let range = nl.start..nl.end;
|
||||||
|
self.is_part_number(range)
|
||||||
|
})
|
||||||
|
.map(|nl| nl.to_number())
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_one() {
|
||||||
|
let grid = Grid::parse(FILE_STR);
|
||||||
|
println!(
|
||||||
|
"Part 1: Sum of part numbers: {}",
|
||||||
|
grid.get_part_number_sum()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
part_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
|
const EXAMPLE_FILE_STR: &'static str = include_str!("example_input.txt");
|
||||||
use super::*;
|
use super::*;
|
||||||
}
|
|
||||||
|
#[test]
|
||||||
|
fn test_adjacent_index() {
|
||||||
|
let grid = Grid::parse(EXAMPLE_FILE_STR);
|
||||||
|
assert_eq!(grid.adjacent_ind(5), HashSet::from([4, 6, 14, 15, 16]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_adjacent() {
|
||||||
|
let grid = Grid::parse(EXAMPLE_FILE_STR);
|
||||||
|
let adjacent = grid.find_adjacent(5..6);
|
||||||
|
let hadj = HashSet::from_iter(adjacent.iter().cloned());
|
||||||
|
assert_eq!(hadj, HashSet::from([ValueType::Empty, Number(1)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_part_numbers() {
|
||||||
|
let grid = Grid::parse(EXAMPLE_FILE_STR);
|
||||||
|
let expected = [467, 35, 633, 617, 592, 755, 664, 598];
|
||||||
|
assert_eq!(grid.get_part_numbers(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_number_sum() {
|
||||||
|
let grid = Grid::parse(EXAMPLE_FILE_STR);
|
||||||
|
assert_eq!(grid.get_part_number_sum(), 4361);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user