day three part two
This commit is contained in:
parent
55dcfcd7e8
commit
d8bfa2e21b
2 changed files with 112 additions and 4 deletions
|
@ -7,9 +7,9 @@ edition = "2021"
|
|||
name = "pone"
|
||||
path = "src/one.rs"
|
||||
|
||||
# [[bin]]
|
||||
# name = "ptwo"
|
||||
# path = "src/two.rs"
|
||||
[[bin]]
|
||||
name = "ptwo"
|
||||
path = "src/two.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
|
110
three/src/two.rs
110
three/src/two.rs
|
@ -1,8 +1,116 @@
|
|||
use std::io::{self, Read};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::{self, Read},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut buf = String::new();
|
||||
io::stdin().read_to_string(&mut buf).unwrap();
|
||||
|
||||
let lines: Vec<String> = buf.lines().map(ToOwned::to_owned).collect();
|
||||
|
||||
let line_number_indecies: Vec<_> = lines.iter().map(get_numbers).collect();
|
||||
|
||||
// tuple of the line number and character indexes for our list of lines
|
||||
let numbers = line_number_indecies
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(line, idxs)| {
|
||||
idxs.iter()
|
||||
.map(AsRange::as_range)
|
||||
.zip(std::iter::repeat(line))
|
||||
});
|
||||
|
||||
let mut star_counter: HashMap<(usize, usize), Vec<NumberPos>> = HashMap::new();
|
||||
for (positions, line) in numbers.clone() {
|
||||
if let Some((star_line, star_pos, number)) = is_near_star(&lines, line, positions.clone()) {
|
||||
star_counter
|
||||
.entry((star_line, star_pos))
|
||||
.and_modify(|numbers| numbers.push(number.clone()))
|
||||
.or_insert(Vec::from([number]));
|
||||
}
|
||||
}
|
||||
|
||||
let total: usize = star_counter
|
||||
.values_mut()
|
||||
.filter(|numbers| numbers.len() == 2)
|
||||
.map(|numbers| {
|
||||
numbers
|
||||
.iter()
|
||||
.map(|(positions, line)| &lines[*line][positions.clone()])
|
||||
.flat_map(str::parse::<usize>)
|
||||
.product::<usize>()
|
||||
})
|
||||
.sum();
|
||||
|
||||
print!("{total}");
|
||||
}
|
||||
|
||||
type NumberPos = (std::ops::Range<usize>, usize);
|
||||
|
||||
fn get_numbers<S>(s: S) -> Vec<(usize, usize)>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let s = s.as_ref();
|
||||
|
||||
let mut number_indecies = Vec::new();
|
||||
|
||||
let mut idx = 0;
|
||||
while idx < s.len() {
|
||||
if s.chars().nth(idx).unwrap().is_ascii_digit() {
|
||||
let start_index = idx;
|
||||
while idx < s.len() && s.chars().nth(idx).unwrap().is_ascii_digit() {
|
||||
idx += 1;
|
||||
}
|
||||
let end_index = idx;
|
||||
|
||||
number_indecies.push((start_index, end_index));
|
||||
} else {
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
number_indecies
|
||||
}
|
||||
|
||||
trait AsRange {
|
||||
fn as_range(&self) -> std::ops::Range<usize>;
|
||||
}
|
||||
|
||||
impl AsRange for (usize, usize) {
|
||||
fn as_range(&self) -> std::ops::Range<usize> {
|
||||
self.0..self.1
|
||||
}
|
||||
}
|
||||
|
||||
fn is_near_star(
|
||||
source: &Vec<String>,
|
||||
number_line: usize,
|
||||
number_positions: std::ops::Range<usize>,
|
||||
) -> Option<(usize, usize, NumberPos)> {
|
||||
let mut star = None;
|
||||
'outer: for line in clamp_range(fuzz_range(number_line..number_line + 1), source.len()) {
|
||||
for position in clamp_range(fuzz_range(number_positions.clone()), source[line].len()) {
|
||||
match source[line].chars().nth(position).unwrap() {
|
||||
'*' => {
|
||||
star = Some((position, line, (number_positions, number_line)));
|
||||
break 'outer;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
star
|
||||
}
|
||||
|
||||
fn clamp_range(input: std::ops::Range<usize>, max: usize) -> std::ops::Range<usize> {
|
||||
let end = if input.end > max { max } else { input.end };
|
||||
|
||||
input.start..end
|
||||
}
|
||||
|
||||
fn fuzz_range(input: std::ops::Range<usize>) -> std::ops::Range<usize> {
|
||||
input.start.checked_sub(1).unwrap_or(input.start)..input.end + 1
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue