Skip to content
Merged

Shape #171

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 9 additions & 5 deletions puzzle_config/src/config/board.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
use crate::config::area::AreaConfig;
use crate::{Target, TargetIndex, TargetTemplate};
use ndarray::Array2;
use puzzled_common::Shape;
use puzzled_common::ShapeType::Square;
use std::hash::{Hash, Hasher};

/// Configuration for the board layout and areas.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum BoardConfig {
Simple {
layout: Array2<bool>,
layout: Shape,
},
Area {
layout: Box<Array2<bool>>,
layout: Box<Shape>,
area_indices: Box<Array2<i32>>,
display_values: Box<Array2<String>>,
value_order: Box<Array2<i32>>,
Expand Down Expand Up @@ -59,7 +61,7 @@ impl BoardConfig {
None
}

pub fn layout(&self) -> &Array2<bool> {
pub fn layout(&self) -> &Shape {
match self {
BoardConfig::Simple { layout } => layout,
BoardConfig::Area { layout, .. } => layout,
Expand Down Expand Up @@ -167,7 +169,7 @@ pub fn from_predefined_board(name: &str) -> Option<BoardConfig> {
.get(0..2)
.map(|dims| (dims[0], dims[1]));
dim.map(|(rows, cols)| BoardConfig::Simple {
layout: Array2::from_shape_fn((rows as usize, cols as usize), |_| true),
layout: Shape::from_elem((rows as usize, cols as usize), Square, true),
})
}

Expand All @@ -177,10 +179,12 @@ mod tests {
use crate::config::area::{AreaConfig, AreaValueFormatter};
use crate::config::target::{TargetIndex, TargetTemplate};
use ndarray::arr2;
use puzzled_common::shape::shape_square;

#[test]
fn test_puzzle_config_get_display_values_for_area() {
let board_layout = arr2(&[[true, true, false], [true, true, true], [false, true, true]]);
let board_layout =
shape_square(&[[true, true, false], [true, true, true], [false, true, true]]);
let area_indices = arr2(&[[0, 0, -1], [0, 1, 1], [-1, 1, 1]]);
let display_values = arr2(&[
["A".to_string(), "B".to_string(), "".to_string()],
Expand Down
20 changes: 10 additions & 10 deletions puzzle_config/src/config/tile.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::config::color::ColorConfig;
use ndarray::Array2;
use puzzled_common::Shape;
use std::hash::{DefaultHasher, Hash, Hasher};

/// Configuration for a tile that can be placed on the board.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TileConfig {
base: Array2<bool>,
base: Shape,
color: ColorConfig,
name: Option<String>,
}
Expand All @@ -18,13 +18,13 @@ impl TileConfig {
/// * `base`: Base shape of the tile as a 2D boolean array.
///
/// returns: TileConfig
pub fn new(base: Array2<bool>, color: ColorConfig, name: Option<String>) -> TileConfig {
pub fn new(base: Shape, color: ColorConfig, name: Option<String>) -> TileConfig {
TileConfig { base, color, name }
}

/// Base shape of the tile as a 2D boolean array.
/// True indicates a filled cell, false indicates an empty cell.
pub fn base(&self) -> &Array2<bool> {
pub fn base(&self) -> &Shape {
&self.base
}

Expand Down Expand Up @@ -69,22 +69,22 @@ impl Hash for TileConfig {
#[cfg(test)]
mod tests {
use super::*;
use ndarray::array;
use puzzled_common::shape::shape_square;

#[test]
fn test_hash() {
let tile1 = TileConfig::new(
array![[true, false], [false, true]],
shape_square(&[[true, false], [false, true]]),
ColorConfig::default_with_index(0),
None,
);
let tile2 = TileConfig::new(
array![[true, false], [false, true]],
shape_square(&[[true, false], [false, true]]),
ColorConfig::default_with_index(1),
None,
);
let tile3 = TileConfig::new(
array![[false, true], [true, false]],
shape_square(&[[false, true], [true, false]]),
ColorConfig::default_with_index(1),
None,
);
Expand All @@ -108,12 +108,12 @@ mod tests {
#[test]
fn test_hash_slice_any_order() {
let tile1 = TileConfig::new(
array![[true, false], [false, true]],
shape_square(&[[true, false], [false, true]]),
ColorConfig::default_with_index(0),
None,
);
let tile2 = TileConfig::new(
array![[false, true], [true, false]],
shape_square(&[[false, true], [true, false]]),
ColorConfig::default_with_index(1),
None,
);
Expand Down
33 changes: 18 additions & 15 deletions puzzle_config/src/json/converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use crate::{
TileConfig,
};
use ndarray::Array2;
use puzzled_common::Shape;
use puzzled_common::ShapeType::Square;
use std::num::NonZero;
use time::OffsetDateTime;

Expand Down Expand Up @@ -99,19 +101,19 @@ fn rotate_board_to_landscape<T>(arr: Array2<T>) -> Array2<T> {

fn rotate_board(board: BoardConfig) -> BoardConfig {
match board {
BoardConfig::Simple { layout } => {
let layout = rotate_board_to_landscape(layout);
BoardConfig::Simple { mut layout } => {
layout.rotate_to_landscape();
BoardConfig::Simple { layout }
}
BoardConfig::Area {
layout,
mut layout,
area_indices,
display_values,
value_order,
area_configs,
target_template,
} => {
let layout = Box::new(rotate_board_to_landscape(*layout));
layout.rotate_to_landscape();
let area_indices = Box::new(rotate_board_to_landscape(*area_indices));
let display_values = Box::new(rotate_board_to_landscape(*display_values));
let value_order = Box::new(rotate_board_to_landscape(*value_order));
Expand Down Expand Up @@ -206,12 +208,12 @@ impl Convertable<Vec<TileConfig>> for (usize, Tile, Option<String>) {
}
}

impl Convertable<(Array2<bool>, Option<String>)> for (usize, TileLayout) {
impl Convertable<(Shape, Option<String>)> for (usize, TileLayout) {
fn convert(
self,
predefined: &Predefined,
custom: &mut Custom,
) -> Result<(Array2<bool>, Option<String>), ReadError> {
) -> Result<(Shape, Option<String>), ReadError> {
match self.1 {
TileLayout::Ref(name) => {
if let Some(custom_tile) = custom.get_tile(&name) {
Expand Down Expand Up @@ -249,13 +251,13 @@ impl Convertable<(Array2<bool>, Option<String>)> for (usize, TileLayout) {
return Err(ReadError::TileWidthOrHeightCannotBeZero);
}
}
let mut base = Array2::<bool>::default((height, width));
let mut base = Shape::from_elem((height, width), Square, false);
for (i, row) in array.iter().enumerate() {
for (j, &value) in row.iter().enumerate() {
base[(i, j)] = value != 0;
}
}
let base = base.reversed_axes();
base.transpose();
Ok((base, None))
}
}
Expand Down Expand Up @@ -305,13 +307,13 @@ impl Convertable<BoardConfig> for Board {
return Err(ReadError::BoardWidthOrHeightCannotBeZero);
}
}
let mut array = Array2::<bool>::default((height, width));
let mut array = Shape::from_elem((height, width), Square, false);
for (i, row) in layout.iter().enumerate() {
for (j, &value) in row.iter().enumerate() {
array[(i, j)] = value < 1;
}
}
let array = array.reversed_axes();
array.transpose();
Ok(BoardConfig::Simple { layout: array })
}
Board::AreaBoard {
Expand All @@ -337,14 +339,15 @@ impl Convertable<BoardConfig> for Board {
return Err(ReadError::BoardWidthOrHeightCannotBeZero);
}
}
let mut array = Array2::<bool>::default((height, width));
let mut array = Shape::from_elem((height, width), Square, false);
for (i, row) in area_layout.iter().enumerate() {
for (j, &value) in row.iter().enumerate() {
array[(i, j)] = value >= 0;
}
}

array.reversed_axes()
array.transpose();
array
};

Ok(BoardConfig::Area {
Expand Down Expand Up @@ -444,7 +447,7 @@ impl Convertable<String> for DefaultFactory {
#[cfg(test)]
mod tests {
use super::*;
use ndarray::arr2;
use puzzled_common::shape::shape_square;

#[test]
fn test_convert_predefined_tile() {
Expand All @@ -459,7 +462,7 @@ mod tests {
.convert(&predefined, &mut Custom::default())
.unwrap();
let expected_tile = TileConfig::new(
arr2(&[[true, false], [true, true]]).reversed_axes(),
shape_square(&[[true, false], [true, true]]).transposed(),
ColorConfig::default_with_index(0),
Some("L3".to_string()),
);
Expand Down Expand Up @@ -487,7 +490,7 @@ mod tests {
.convert(&Predefined::default(), &mut Custom::default())
.unwrap();
let expected_tile = TileConfig::new(
arr2(&[[true, false], [true, true]]).reversed_axes(),
shape_square(&[[true, false], [true, true]]).transposed(),
ColorConfig::default_with_index(0),
Some("L3".to_string()),
);
Expand Down
8 changes: 4 additions & 4 deletions puzzle_config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl Predefined {
#[cfg(test)]
mod tests {
use crate::create_json_loader;
use ndarray::arr2;
use puzzled_common::shape::shape_square;

#[test]
fn test_load_puzzle_collection_from_json() {
Expand Down Expand Up @@ -125,17 +125,17 @@ mod tests {
assert_eq!(2, puzzle.tiles().len());
assert_eq!(
puzzle.board_config().layout(),
arr2(&[[true, true, true], [true, false, true], [true, true, true]])
&shape_square(&[[true, true, true], [true, false, true], [true, true, true]])
);
let ref_tile = &puzzle.tiles()[0];
assert_eq!(
ref_tile.base(),
arr2(&[[true, false], [true, true]]).reversed_axes()
&shape_square(&[[true, true], [false, true]])
);
let custom_tile = &puzzle.tiles()[1];
assert_eq!(
custom_tile.base(),
arr2(&[[true, false, true], [true, true, true]]).reversed_axes()
&shape_square(&[[true, true], [false, true], [true, true]])
);
}
}
19 changes: 9 additions & 10 deletions puzzle_config/src/random/growing.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
use crate::random::{Algorithm, RandomPuzzleSettings};
use ndarray::Array2;
use puzzled_common::array_util;
use puzzled_common::Shape;
use puzzled_common::ShapeType::Square;
use rand::{Rng, RngExt};
use std::collections::BTreeMap;

pub fn create_puzzle(
settings: &RandomPuzzleSettings,
rng: &mut dyn Rng,
) -> (Array2<bool>, Vec<Array2<bool>>) {
pub fn create_puzzle(settings: &RandomPuzzleSettings, rng: &mut dyn Rng) -> (Shape, Vec<Shape>) {
let tile_count = match settings.algorithm {
Algorithm::Growing { tile_count, .. } => tile_count,
};
let base_board = generate_base_board(settings, rng);
let complete_board = grow_until_complete(rng, base_board);

let board = complete_board.map(|x| x.is_some());
let board = Shape::new(Square, complete_board.map(|x| x.is_some()));
let tiles = (0..tile_count)
.map(|i| extract_tile(i as u32, &complete_board))
.filter(|tile| tile.iter().any(|&x| x))
Expand Down Expand Up @@ -128,8 +126,9 @@ fn grow_tile_index(
(changed, new_board)
}

fn extract_tile(tile_index: u32, complete_board: &Array2<Option<u32>>) -> Array2<bool> {
let mut base = complete_board.map(|&x| x.filter(|&i| i == tile_index).is_none());
array_util::remove_true_rows_cols_from_sides(&mut base);
base.map(|x| !x)
fn extract_tile(tile_index: u32, complete_board: &Array2<Option<u32>>) -> Shape {
let base = complete_board.map(|&x| x.filter(|&i| i == tile_index).is_none());
let mut shape = Shape::new(Square, base);
shape.trim_matching(true);
shape.map(|x| !x)
}
Loading
Loading