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)
}