Hi,大家好,这里是炼手Rust专栏,我是xiaoK,今天来看个编程挑战:扫雷。
提问
扫雷的规则大家都不陌生,不过今天我们不是为了解决扫雷,而是按照游戏的规则标识雷的位置。
你的任务是将地雷数量添加到已完成的扫雷板中的空方块中,棋盘本身是一个由空(' ')或地雷('*')的方块组成的矩形。
对于每个空方格,计算与其相邻的地雷数量(水平、垂直、对角线)。如果空方格没有相邻的地雷,则将其留空。否则用相邻的地雷数量替换它。
例:
对于这样的 5 x 4 棋盘(此处用'·'字符表示空白以在屏幕上显示):
input:
·*·*·
··*··
··*··
·····
你需要转换成如下形式:
output:
1*3*1
13*31
·2*2·
·111·
将棋盘转换成我们玩儿扫雷游戏的那样,标识出周边地雷的位置。
模板:
pub fn annotate(minefield: &[&str]) -> Vec<String> { }
分析
对于输入字串,我们先按照换行符进行分割,然后按照字符char进行分割,将其转完成一个二维数组,然后遍历这个二维数组,对于每一个空位位置(即图中标识的'.'的位置),对其周边计算雷'*'的数量,得到的结果,设置给该位置即可。
解决方案
pub fn annotate(minefield: &[&str]) -> Vec<String> {
if minefield.is_empty() { return vec![]; }
let mut mines: Vec<Vec<char>> = minefield.iter().map(|s| (**s).chars().collect::<Vec<char>>()).collect();
let rows = mines.len();
let cols = mines[0].len();
let def = ' ';
(0..rows).for_each(|r| {
(0..cols).for_each(|c| {
if mines[r][c] == ' ' {
let mut sum: u8 = 0;
(r.saturating_sub(1)..=(r + 1)).for_each(|mine_row| {
if let Some(row) = mines.get(mine_row) {
(c.saturating_sub(1)..=(c + 1)).for_each(|mine_col| {
if *(row.get(mine_col).unwrap_or(&def)) == '*' {
sum += 1;
}
})
}
});
if sum != 0 {
mines[r][c] = char::from(b'0' + sum);
}
}
});
});
mines.iter().map(|chs| chs.iter().collect::<String>()).collect()
}
Trick
saturating_sub可以得到不越界的减法运算。在进行遍历的时候,可以使用这个方法进行不越界的遍历。- 在
rust中,有一种方法可以快速的得到String类型以及char类型的byte数组——使用前置b。例如:b'H'就是一个u8类型,而b"Hello"就是一个&[u8; 5]类型。