Notes on codes, projects and everything

Learning a new language for more than 2 months (feat. Exercism)

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

leave your comment

name is required

email is required

have a blog?

This blog uses scripts to assist and automate comment moderation, and the author of this blog post does not hold responsibility in the content of posted comments. Please note that activities such as flaming, ungrounded accusations as well as spamming will not be entertained.

Pings

Click to change color scheme