I'm following the Rust-wasm tutorial and I want to be able to easily add a ship (a shape really) to the Universe in the game of life.
As a first step, I'd like to convert a 2-dimensional array of 0
or 1
representing a shape into a vector of indices representing the coordinates of the shape in the Universe.
I have a working piece of code but I'd like to make it a bit more user-friendly:
const WIDTH: u32 = 64;
const HEIGHT: u32 = 64;
/// glider: [[0, 1, 0], [0, 0, 1], [1, 1, 1]]
fn make_ship(shape: Vec<Vec<u32>>) -> Vec<u32> {
let mut ship: Vec<u32> = Vec::new();
for row_idx in 0..shape.len() {
for col_idx in 0..shape[row_idx].len() {
let cell = shape[row_idx][col_idx];
if cell == 1 {
ship.push(col_idx as u32 + row_idx as u32 * WIDTH);
}
}
}
ship
}
#[test]
fn glider() {
let glider = vec![vec![0, 1, 0], vec![0, 0, 1], vec![1, 1, 1]];
println!("{:?}", make_ship(glider));
}
The test
shows my problem: the verbosity of vec!
s. Ideally I'd like to be able to write it without all the vec!
. The code of make_ship
shouldn't care about the size of the shape arrays. Ideal example:
let glider = [[0, 1, 0],
[0, 0, 1],
[1, 1, 1],];
The question is: how to express a shape nicely with simple arrays and have the function make_ship
take 2-dimensional vectors of arbitrary size?
Reducing the number of vec!
s is possible with a custom macro:
#[macro_export]
macro_rules! vec2d {
($($i:expr),+) => { // handle numbers
{
let mut ret = Vec::new();
$(ret.push($i);)*
ret
}
};
([$($arr:tt),+]) => { // handle sets
{
let mut ret = Vec::new();
$(ret.push(vec!($arr));)*
ret
}
};
}
fn main() {
let glider = vec2d![[0, 1, 0],
[0, 0, 1],
[1, 1, 1]];
let glider2 = vec2d![[0, 1, 0, 1],
[0, 0, 1, 0],
[1, 1, 1, 0],
[0, 1, 1, 0]];
println!("{:?}", glider); // [[0, 1, 0], [0, 0, 1], [1, 1, 1]]
println!("{:?}", glider2); // [[0, 1, 0, 1], [0, 0, 1, 0], [1, 1, 1, 0], [0, 1, 1, 0]]
}
Your initial function could also use a bit of an improvement with the help of Rust's iterators:
fn make_ship(shape: Vec<Vec<u32>>) -> Vec<u32> {
shape
.iter()
.enumerate()
.flat_map(|(row, v)| {
v.iter().enumerate().filter_map(move |(col, x)| {
if *x == 1 {
Some(col as u32 + row as u32 * WIDTH)
} else {
None
}
})
})
.collect()
}