155 lines
4.6 KiB
Rust
155 lines
4.6 KiB
Rust
#[derive(Copy, Clone, Debug)]
|
|
pub struct Tile {
|
|
pub start_column: usize,
|
|
pub end_column: usize,
|
|
pub start_row: usize,
|
|
pub end_row: usize,
|
|
}
|
|
|
|
impl Tile {
|
|
pub fn width(&self) -> usize {
|
|
self.end_column - self.start_column
|
|
}
|
|
pub fn height(&self) -> usize {
|
|
self.end_row - self.start_row
|
|
}
|
|
}
|
|
|
|
pub struct TileIterator {
|
|
tile_size: usize,
|
|
total_height: usize,
|
|
total_width: usize,
|
|
current_column: usize,
|
|
current_row: usize,
|
|
}
|
|
|
|
impl TileIterator {
|
|
pub fn new(total_width: usize, total_height: usize, tile_size: usize) -> TileIterator {
|
|
// If tile_size*2 is greater than usize::max_value(), increment would overflow
|
|
assert!(tile_size > 0 && tile_size * 2 < usize::max_value());
|
|
TileIterator {
|
|
tile_size,
|
|
total_width,
|
|
total_height,
|
|
current_column: 0,
|
|
current_row: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Iterator for TileIterator {
|
|
type Item = Tile;
|
|
|
|
fn next(&mut self) -> Option<Tile> {
|
|
if self.current_row >= self.total_height {
|
|
None
|
|
} else {
|
|
let start_column = self.current_column;
|
|
let end_column = self.total_width.min(start_column + self.tile_size);
|
|
let start_row = self.current_row;
|
|
let end_row = self.total_height.min(start_row + self.tile_size);
|
|
|
|
self.current_column += self.tile_size;
|
|
if self.current_column >= self.total_width {
|
|
self.current_row += self.tile_size;
|
|
self.current_column = 0;
|
|
}
|
|
|
|
Some(Tile {
|
|
start_column,
|
|
end_column,
|
|
start_row,
|
|
end_row,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
use quickcheck::TestResult;
|
|
use quickcheck_macros::quickcheck;
|
|
|
|
#[test]
|
|
fn iterator_has_correct_number_of_tiles_when_dimensions_are_multiple_of_tile_size() {
|
|
let target = TileIterator::new(20, 15, 5);
|
|
assert!(target.count() == 12);
|
|
}
|
|
|
|
#[test]
|
|
fn iterator_has_correct_number_of_tiles_when_width_is_one_under_multiple_of_tile_size() {
|
|
let target = TileIterator::new(19, 15, 5);
|
|
assert!(target.count() == 12);
|
|
}
|
|
|
|
#[test]
|
|
fn iterator_has_correct_number_of_tiles_when_width_is_one_over_multiple_of_tile_size() {
|
|
let target = TileIterator::new(21, 15, 5);
|
|
assert!(target.count() == 15);
|
|
}
|
|
|
|
#[test]
|
|
fn iterator_has_correct_number_of_tiles_when_height_is_one_under_multiple_of_tile_size() {
|
|
let target = TileIterator::new(20, 14, 5);
|
|
assert!(target.count() == 12);
|
|
}
|
|
|
|
#[test]
|
|
fn iterator_has_correct_number_of_tiles_when_height_is_one_over_multiple_of_tile_size() {
|
|
let target = TileIterator::new(20, 16, 5);
|
|
assert!(target.count() == 16);
|
|
}
|
|
|
|
#[quickcheck]
|
|
fn tiles_are_expected_size(width: usize, height: usize, tile_size: usize) -> TestResult {
|
|
let max_size = 10000;
|
|
// Check size of width and height first, since width*height might overflow.
|
|
if width > max_size || height > max_size || width * height > max_size {
|
|
return TestResult::discard();
|
|
}
|
|
if tile_size == 0 {
|
|
return TestResult::discard();
|
|
}
|
|
|
|
let mut target = TileIterator::new(width, height, tile_size);
|
|
TestResult::from_bool(target.all(|tile| {
|
|
tile.end_column - tile.start_column <= tile_size
|
|
&& tile.end_row - tile.start_row <= tile_size
|
|
}))
|
|
}
|
|
|
|
#[quickcheck]
|
|
fn iterator_includes_all_coordinates_exactly_once(
|
|
width: usize,
|
|
height: usize,
|
|
tile_size: usize,
|
|
) -> TestResult {
|
|
let max_size = 10000;
|
|
// Check size of width and height first, since width*height might overflow.
|
|
if width > max_size || height > max_size || width * height > max_size {
|
|
return TestResult::discard();
|
|
}
|
|
if tile_size == 0 {
|
|
return TestResult::discard();
|
|
}
|
|
|
|
let target = TileIterator::new(width, height, tile_size);
|
|
let mut index_counts = vec![0; width * height];
|
|
let mut total_count = 0;
|
|
for tile in target {
|
|
for column in tile.start_column..tile.end_column {
|
|
for row in tile.start_row..tile.end_row {
|
|
index_counts[row * width + column] += 1;
|
|
total_count += 1;
|
|
if total_count > width * height {
|
|
return TestResult::failed();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
TestResult::from_bool(index_counts.iter().all(|&elem| elem == 1))
|
|
}
|
|
}
|