Day 4 part 1 complete

This commit is contained in:
2025-05-28 20:45:26 -05:00
parent 724ac02a9f
commit ce72a9b3cf
3 changed files with 367 additions and 71 deletions

View File

@@ -10,25 +10,99 @@ pub struct Crossword {
height: usize,
}
pub struct WordPuller<'a> {
pub struct CrosswordCell<'a> {
x: usize,
y: usize,
direction: (isize, isize),
crossword: &'a Crossword,
}
impl<'a> WordPuller<'a> {
pub fn take(&self, num_of_letters: usize) -> String {
impl<'a> CrosswordCell<'a> {
fn value_by_index(&self, x: usize, y: usize) -> char {
// x and y are reverse because we store by row
//
// data[ROW][COLUMN]
self.crossword.data[y][x]
}
pub fn value(&self) -> char {
self.value_by_index(self.x, self.y)
}
fn take(&self, num_of_letters: usize, direction: (isize, isize)) -> String {
let mut word = String::new();
for i in 0..num_of_letters {
let x = self.x as isize + (i as isize * self.direction.0);
let y = self.y as isize + (i as isize * self.direction.1);
word.push(self.crossword.data[x as usize][y as usize]);
let x = self.x as isize + (i as isize * direction.1);
let y = self.y as isize + (i as isize * direction.0);
if x < 0
|| x >= self.crossword.width as isize
|| y < 0
|| y >= self.crossword.height as isize
{
break;
}
word.push(self.value_by_index(x as usize, y as usize));
}
word
}
pub fn n(&self, num_of_letters: usize) -> String {
self.take(num_of_letters, (-1, 0))
}
pub fn ne(&self, num_of_letters: usize) -> String {
self.take(num_of_letters, (-1, 1))
}
pub fn e(&self, num_of_letters: usize) -> String {
self.take(num_of_letters, (0, 1))
}
pub fn se(&self, num_of_letters: usize) -> String {
self.take(num_of_letters, (1, 1))
}
pub fn s(&self, num_of_letters: usize) -> String {
self.take(num_of_letters, (1, 0))
}
pub fn sw(&self, num_of_letters: usize) -> String {
self.take(num_of_letters, (1, -1))
}
pub fn w(&self, num_of_letters: usize) -> String {
self.take(num_of_letters, (0, -1))
}
pub fn nw(&self, num_of_letters: usize) -> String {
self.take(num_of_letters, (-1, -1))
}
}
pub struct CrosswordIterator<'a> {
crossword: &'a Crossword,
position: usize,
}
impl<'a> Iterator for CrosswordIterator<'a> {
type Item = CrosswordCell<'a>;
fn next(&mut self) -> Option<Self::Item> {
let y = self.position / self.crossword.width;
let x = self.position - y * self.crossword.width;
self.position += 1;
if x < self.crossword.width && y < self.crossword.height {
Some(CrosswordCell {
x,
y,
crossword: self.crossword,
})
} else {
None
}
}
}
impl Crossword {
@@ -41,6 +115,8 @@ impl Crossword {
input_buffer.read_line(&mut first_line)?;
let first_line = first_line.trim_end().to_owned();
let width = first_line.len();
data.push(first_line.chars().collect());
for line in input_buffer.lines() {
let line = line?;
if line.len() != width {
@@ -58,6 +134,16 @@ impl Crossword {
})
}
fn from_data(data: Vec<Vec<char>>) -> Crossword {
let height = data.len();
let width = data[0].len();
Crossword {
data,
width,
height,
}
}
pub fn width(&self) -> usize {
self.width
}
@@ -66,67 +152,17 @@ impl Crossword {
self.height
}
pub fn n(&self, x: usize, y: usize) -> WordPuller {
WordPuller {
x,
y,
direction: (-1, 0),
pub fn iter_cells(&self) -> CrosswordIterator {
CrosswordIterator {
crossword: self,
position: 0,
}
}
pub fn ne(&self, x: usize, y: usize) -> WordPuller {
WordPuller {
pub fn select_cell(&self, x: usize, y: usize) -> CrosswordCell {
CrosswordCell {
x,
y,
direction: (-1, 1),
crossword: self,
}
}
pub fn e(&self, x: usize, y: usize) -> WordPuller {
WordPuller {
x,
y,
direction: (0, 1),
crossword: self,
}
}
pub fn se(&self, x: usize, y: usize) -> WordPuller {
WordPuller {
x,
y,
direction: (1, 1),
crossword: self,
}
}
pub fn s(&self, x: usize, y: usize) -> WordPuller {
WordPuller {
x,
y,
direction: (1, 0),
crossword: self,
}
}
pub fn sw(&self, x: usize, y: usize) -> WordPuller {
WordPuller {
x,
y,
direction: (1, -1),
crossword: self,
}
}
pub fn w(&self, x: usize, y: usize) -> WordPuller {
WordPuller {
x,
y,
direction: (0, -1),
crossword: self,
}
}
pub fn nw(&self, x: usize, y: usize) -> WordPuller {
WordPuller {
x,
y,
direction: (-1, -1),
crossword: self,
}
}
@@ -141,3 +177,89 @@ impl Display for Crossword {
Ok(())
}
}
#[cfg(test)]
mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope.
use super::*;
fn small_test_crossword() -> Crossword {
Crossword::from_data(vec![
vec!['1', '2', '3'],
vec!['4', '5', '6'],
vec!['7', '8', '9'],
])
}
#[test]
fn test_north() {
let crossword = small_test_crossword();
assert_eq!(crossword.select_cell(1, 1).n(0), "");
assert_eq!(crossword.select_cell(1, 1).n(1), "5");
assert_eq!(crossword.select_cell(1, 1).n(2), "52");
assert_eq!(crossword.select_cell(1, 1).n(3), "52");
}
#[test]
fn test_northeast() {
let crossword = small_test_crossword();
assert_eq!(crossword.select_cell(1, 1).ne(0), "");
assert_eq!(crossword.select_cell(1, 1).ne(1), "5");
assert_eq!(crossword.select_cell(1, 1).ne(2), "53");
assert_eq!(crossword.select_cell(1, 1).ne(3), "53");
}
#[test]
fn test_east() {
let crossword = small_test_crossword();
assert_eq!(crossword.select_cell(1, 1).e(0), "");
assert_eq!(crossword.select_cell(1, 1).e(1), "5");
assert_eq!(crossword.select_cell(1, 1).e(2), "56");
assert_eq!(crossword.select_cell(1, 1).e(3), "56");
}
#[test]
fn test_southeast() {
let crossword = small_test_crossword();
assert_eq!(crossword.select_cell(1, 1).se(0), "");
assert_eq!(crossword.select_cell(1, 1).se(1), "5");
assert_eq!(crossword.select_cell(1, 1).se(2), "59");
assert_eq!(crossword.select_cell(1, 1).se(3), "59");
}
#[test]
fn test_south() {
let crossword = small_test_crossword();
assert_eq!(crossword.select_cell(1, 1).s(0), "");
assert_eq!(crossword.select_cell(1, 1).s(1), "5");
assert_eq!(crossword.select_cell(1, 1).s(2), "58");
assert_eq!(crossword.select_cell(1, 1).s(3), "58");
}
#[test]
fn test_southwest() {
let crossword = small_test_crossword();
assert_eq!(crossword.select_cell(1, 1).sw(0), "");
assert_eq!(crossword.select_cell(1, 1).sw(1), "5");
assert_eq!(crossword.select_cell(1, 1).sw(2), "57");
assert_eq!(crossword.select_cell(1, 1).sw(3), "57");
}
#[test]
fn test_west() {
let crossword = small_test_crossword();
assert_eq!(crossword.select_cell(1, 1).w(0), "");
assert_eq!(crossword.select_cell(1, 1).w(1), "5");
assert_eq!(crossword.select_cell(1, 1).w(2), "54");
assert_eq!(crossword.select_cell(1, 1).w(3), "54");
}
#[test]
fn test_northwest() {
let crossword = small_test_crossword();
assert_eq!(crossword.select_cell(1, 1).nw(0), "");
assert_eq!(crossword.select_cell(1, 1).nw(1), "5");
assert_eq!(crossword.select_cell(1, 1).nw(2), "51");
assert_eq!(crossword.select_cell(1, 1).nw(3), "51");
}
}

View File

@@ -11,6 +11,10 @@ mod crossword;
use crate::crossword::Crossword;
fn main() -> Result<(), Box<dyn Error>> {
unsafe {
env::set_var("RUST_BACKTRACE", "1");
}
// Handle command input
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
@@ -25,14 +29,44 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("{}", crossword);
println!("n: {}", crossword.n(3, 3).take(4));
println!("ne: {}", crossword.ne(3, 3).take(4));
println!("e: {}", crossword.e(3, 3).take(4));
println!("se: {}", crossword.se(3, 3).take(4));
println!("s: {}", crossword.s(3, 3).take(4));
println!("sw: {}", crossword.sw(3, 3).take(4));
println!("w: {}", crossword.w(3, 3).take(4));
println!("nw: {}", crossword.nw(3, 3).take(4));
let word_to_find = "XMAS".to_owned();
let word_to_find_rev = word_to_find.chars().rev().collect::<String>();
let word_len = word_to_find.len();
let mut count = 0;
for cell in crossword.iter_cells() {
// We only need to check forwards and down, as up and back
// will be covered by other cells. If we counted them it
// would cause double counting
// East
let word = cell.e(word_len);
if word == word_to_find || word == word_to_find_rev {
count += 1
}
// Southeast
let word = cell.se(word_len);
if word == word_to_find || word == word_to_find_rev {
count += 1
}
// South
let word = cell.s(word_len);
if word == word_to_find || word == word_to_find_rev {
count += 1
}
// Southwest
let word = cell.sw(word_len);
if word == word_to_find || word == word_to_find_rev {
count += 1
}
}
println!(
"Count of the work '{}' in crossword: {}",
word_to_find, count
);
Ok(())
}