Minesweeper
I have the tendency of abusing some language structure that I really for other purposes that it is not designed for. Previously it was the pattern matching statement, this time it is the Option enum.
pub fn annotate(minefield: &[&str]) -> Vec<String> { let minefield: Vec<Vec<u8>> = minefield.iter().map(|&x| Vec::from(x)).collect(); minefield .iter() .cloned() .enumerate() .map(|(row_idx, row)| { row.iter() .enumerate() .map(|(col_idx, &cell)| { Some(cell) .filter(|&x| x == b' ') .map(|_| match count(&minefield, row_idx, col_idx) { 0 => String::from(" "), n => n.to_string(), }).unwrap_or((cell as char).to_string()) }).collect::<String>() }).collect() } fn count(minefield: &Vec<Vec<u8>>, row_idx: usize, col_idx: usize) -> usize { neighbours(&minefield, row_idx, col_idx) .iter() .filter(|(row_idx, col_idx)| minefield[*row_idx][*col_idx] == b'*') .count() } fn neighbours(minefield: &Vec<Vec<u8>>, row_idx: usize, col_idx: usize) -> Vec<(usize, usize)> { (row_idx.saturating_sub(1)..=max(row_idx, minefield.len())) .flat_map(|r_idx| { (col_idx.saturating_sub(1)..=max(col_idx, minefield.first().map_or(0, |x| x.len()))) .map(move |c_idx| (r_idx, c_idx)) }).filter(|neighbour| *neighbour != (row_idx, col_idx)) .collect() } fn max(idx: usize, length: usize) -> usize { (idx + 1).min(length - 1) }
While I am relatively more comfortable with borrow checker now compared to when I first started, but the same thing cannot be said to dealing with life times. Till now I still find it to be the most complicated thing that I cannot really explain it to other people. Being mostly a programmer in higher level languages where garbage collection is in place, I am very spoilt for not having to even think about them.
I didn’t even care about stack and heap when I first started as they are abstracted in the languages i used.
pub fn annotate(minefield: &[&str]) -> Vec<String> { minefield .iter() .enumerate() .map(|(row_idx, row)| { row.chars() .enumerate() .map( |(col_idx, cell)| match (cell, count(&minefield, row_idx, col_idx)) { (' ', n) if n != 0 => n.to_string(), _ => cell.to_string(), }, ).collect::<String>() }).collect() } fn count(minefield: &[&str], row_idx: usize, col_idx: usize) -> usize { neighbours(&minefield, row_idx, col_idx) .filter(|(row_idx, col_idx)| { minefield[*row_idx] .chars() .nth(*col_idx) .map_or(false, |x| x == '*') }).count() } fn neighbours<'a>( minefield: &'a [&str], row_idx: usize, col_idx: usize, ) -> impl Iterator<Item = (usize, usize)> + 'a { (row_idx.saturating_sub(1)..=max(row_idx, minefield.len())) .flat_map(move |r_idx| { (col_idx.saturating_sub(1)..=max(col_idx, minefield.first().map_or(0, |x| x.len()))) .map(move |c_idx| (r_idx, c_idx)) }).filter(move |neighbour| *neighbour != (row_idx, col_idx)) } fn max(idx: usize, length: usize) -> usize { (idx + 1).min(length - 1) }