added cargo docs and updated constraint and tests
This commit is contained in:
parent
08319e6445
commit
ebedbfbe59
2 changed files with 16169 additions and 24 deletions
193
src/main.rs
193
src/main.rs
|
|
@ -1,9 +1,38 @@
|
|||
use std::collections::HashMap;
|
||||
use rand::{distributions::{Distribution, WeightedIndex}, rngs::ThreadRng, thread_rng};
|
||||
|
||||
/// Represents a tile in the grid.
|
||||
type Tile = i32;
|
||||
|
||||
/// A collection of possible states a cell can be in.
|
||||
type PossibleStates = Vec<Tile>;
|
||||
|
||||
/// A collection of positions relative to the current cell
|
||||
enum ConstraintRelativePos {
|
||||
Top,
|
||||
Bottom,
|
||||
Left,
|
||||
Right,
|
||||
TopLeft,
|
||||
TopRight,
|
||||
BottomLeft,
|
||||
BottomRight
|
||||
}
|
||||
|
||||
enum ConstraintRule {
|
||||
IsEqual,
|
||||
IsNotEqual
|
||||
}
|
||||
|
||||
struct Constraint {
|
||||
relative_pos: ConstraintRelativePos,
|
||||
rule: ConstraintRule,
|
||||
current_tile: Tile,
|
||||
adjacent_tiles: PossibleStates
|
||||
}
|
||||
|
||||
|
||||
/// Represents a cell in the grid.
|
||||
#[derive(Clone)]
|
||||
struct Cell {
|
||||
possible_states: PossibleStates,
|
||||
|
|
@ -12,6 +41,15 @@ struct Cell {
|
|||
}
|
||||
|
||||
impl Cell {
|
||||
/// Creates a new cell with given possible states.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `possible_states` - A vector of possible states the cell can take.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A new `Cell` instance.
|
||||
fn new(possible_states: PossibleStates) -> Self {
|
||||
let entropy = calculate_entropy(&possible_states);
|
||||
Cell {
|
||||
|
|
@ -22,26 +60,39 @@ impl Cell {
|
|||
}
|
||||
}
|
||||
|
||||
/// Information about a cell, specifically for use in a min-heap.
|
||||
#[derive(Clone)]
|
||||
struct CellInfo {
|
||||
entropy: f64,
|
||||
coordinates: (usize, usize),
|
||||
}
|
||||
|
||||
/// A min-heap data structure for managing cells based on their entropy.
|
||||
struct MinHeap {
|
||||
data: Vec<CellInfo>,
|
||||
}
|
||||
|
||||
impl MinHeap {
|
||||
/// Creates a new, empty `MinHeap`.
|
||||
fn new() -> Self {
|
||||
MinHeap { data: Vec::new() }
|
||||
}
|
||||
|
||||
/// Adds a `CellInfo` element to the heap.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `element` - The `CellInfo` to be added.
|
||||
fn add(&mut self, element: CellInfo) {
|
||||
self.data.push(element);
|
||||
self.heapify_up(self.data.len() - 1);
|
||||
}
|
||||
|
||||
/// Removes and returns the smallest `CellInfo` from the heap.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The smallest `CellInfo`, if the heap is not empty.
|
||||
fn pop(&mut self) -> Option<CellInfo> {
|
||||
if self.data.is_empty() {
|
||||
return None;
|
||||
|
|
@ -53,6 +104,11 @@ impl MinHeap {
|
|||
result
|
||||
}
|
||||
|
||||
/// Maintains the heap property by moving an element up.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `index` - The index of the element to be moved up.
|
||||
fn heapify_up(&mut self, mut index: usize) {
|
||||
while index != 0 {
|
||||
let parent_index = (index - 1) / 2;
|
||||
|
|
@ -63,6 +119,11 @@ impl MinHeap {
|
|||
}
|
||||
}
|
||||
|
||||
/// Maintains the heap property by moving an element down.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `index` - The index of the element to be moved down.
|
||||
fn heapify_down(&mut self, mut index: usize) {
|
||||
let length = self.data.len();
|
||||
loop {
|
||||
|
|
@ -89,6 +150,15 @@ impl MinHeap {
|
|||
}
|
||||
}
|
||||
|
||||
/// Calculates the Shannon entropy of a set of tiles.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `data` - A slice of `Tile` instances.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The calculated entropy as a `f64`.
|
||||
fn calculate_entropy(data: &[Tile]) -> f64 {
|
||||
let mut frequency_map = HashMap::new();
|
||||
for &value in data {
|
||||
|
|
@ -103,16 +173,29 @@ fn calculate_entropy(data: &[Tile]) -> f64 {
|
|||
entropy
|
||||
}
|
||||
|
||||
/// Represents the entire grid of cells.
|
||||
struct Grid {
|
||||
cells: Vec<Vec<Cell>>,
|
||||
}
|
||||
|
||||
impl Grid {
|
||||
/// Creates a new grid with specified dimensions and possible states for each cell.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `width` - The width of the grid.
|
||||
/// * `height` - The height of the grid.
|
||||
/// * `possible_states` - Possible states for each cell.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A new `Grid` instance.
|
||||
fn new(width: usize, height: usize, possible_states: PossibleStates) -> Self {
|
||||
let cells = vec![vec![Cell::new(possible_states.clone()); width]; height];
|
||||
Grid { cells }
|
||||
}
|
||||
|
||||
/// Observes the grid and collapses cells based on their states and neighbors.
|
||||
fn observe_and_collapse(&mut self) {
|
||||
let mut rng = thread_rng();
|
||||
|
||||
|
|
@ -139,6 +222,11 @@ impl Grid {
|
|||
}
|
||||
|
||||
|
||||
/// Finds the cell with minimum entropy.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The coordinates of the cell with the minimum entropy.
|
||||
fn find_min_entropy_cell(&self) -> Option<(usize, usize)> {
|
||||
let mut heap = MinHeap::new();
|
||||
|
||||
|
|
@ -156,6 +244,13 @@ impl Grid {
|
|||
heap.pop().map(|cell_info| cell_info.coordinates)
|
||||
}
|
||||
|
||||
/// Collapses a cell to a single state.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x` - The x-coordinate of the cell.
|
||||
/// * `y` - The y-coordinate of the cell.
|
||||
/// * `rng` - A mutable reference to a random number generator.
|
||||
fn collapse_cell(&mut self, x: usize, y: usize, rng: &mut ThreadRng) {
|
||||
let cell = &mut self.cells[y][x];
|
||||
|
||||
|
|
@ -171,38 +266,55 @@ impl Grid {
|
|||
cell.entropy = 0.0;
|
||||
cell.collapsed = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Updates the possible states of a cell based on its neighbors.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x` - The x-coordinate of the cell.
|
||||
/// * `y` - The y-coordinate of the cell.
|
||||
/// * `current_states` - The current possible states of the cell.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Updated possible states after considering neighbors.
|
||||
fn updated_possible_states_based_on_neighbors(&self, x: usize, y: usize, current_states: &PossibleStates) -> PossibleStates {
|
||||
let mut neighbors = Vec::new();
|
||||
|
||||
if y > 0 { neighbors.push(&self.cells[y - 1][x]); }
|
||||
if y < self.cells.len() - 1 { neighbors.push(&self.cells[y + 1][x]); }
|
||||
if x > 0 { neighbors.push(&self.cells[y][x - 1]); }
|
||||
if x < self.cells[0].len() - 1 { neighbors.push(&self.cells[y][x + 1]); }
|
||||
|
||||
let filtered_states: PossibleStates = current_states.iter()
|
||||
.filter(|&&state| {
|
||||
neighbors.iter().all(|neighbor| {
|
||||
!neighbor.collapsed || (neighbor.collapsed && neighbor.possible_states != vec![state])
|
||||
})
|
||||
|
||||
// Assuming self.cells is a 2D grid of cells
|
||||
// Collect the states of neighboring cells if they are collapsed
|
||||
if y > 0 && self.cells[y - 1][x].collapsed {
|
||||
neighbors.push(self.cells[y - 1][x].possible_states[0]);
|
||||
}
|
||||
if y < self.cells.len() - 1 && self.cells[y + 1][x].collapsed {
|
||||
neighbors.push(self.cells[y + 1][x].possible_states[0]);
|
||||
}
|
||||
if x > 0 && self.cells[y][x - 1].collapsed {
|
||||
neighbors.push(self.cells[y][x - 1].possible_states[0]);
|
||||
}
|
||||
if x < self.cells[0].len() - 1 && self.cells[y][x + 1].collapsed {
|
||||
neighbors.push(self.cells[y][x + 1].possible_states[0]);
|
||||
}
|
||||
|
||||
// Filter the states based on the constraints with the neighbors
|
||||
current_states.iter().cloned().filter(|&state| {
|
||||
neighbors.iter().all(|&neighbor_state| {
|
||||
is_constraint_satisfied(&state, &neighbor_state)
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
// println!("Cell[{}, {}] - Before: {:?}, After: {:?}", x, y, current_states, filtered_states);
|
||||
filtered_states
|
||||
}).collect()
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Generates the final state of the grid.
|
||||
fn generate(&mut self) {
|
||||
while self.cells.iter().any(|row| row.iter().any(|cell| !cell.collapsed)) {
|
||||
self.observe_and_collapse();
|
||||
|
||||
for row in &mut self.cells {
|
||||
for cell in row {
|
||||
if cell.possible_states.is_empty() && !cell.collapsed {
|
||||
cell.possible_states = vec![0];
|
||||
cell.possible_states = vec![99999];
|
||||
cell.collapsed = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -210,21 +322,54 @@ impl Grid {
|
|||
}
|
||||
}
|
||||
|
||||
/// Prints the current state of the grid.
|
||||
fn print_grid(&self) {
|
||||
for row in &self.cells {
|
||||
for cell in row {
|
||||
let state = cell.possible_states.get(0).map_or('0', |&s| {
|
||||
if s == 0 { '0' } else { char::from_digit(s as u32, 10).unwrap_or('?') }
|
||||
});
|
||||
print!("{} ", state);
|
||||
let state = cell.possible_states.get(0).map_or(0, |&s| s);
|
||||
print!("{:05} ", state); // Print each state as a 2-digit number with leading zeros if necessary
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Checks if the constraint between two cells is satisfied.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `cell_state` - The state of the current cell.
|
||||
/// * `neighbor_state` - The state of the neighboring cell.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` if the constraint is satisfied, `false` otherwise.
|
||||
fn is_constraint_satisfied(cell_state: &Tile, neighbor_state: &Tile) -> bool {
|
||||
// Check if the neighbor's state is either one more or one less than the current cell's state
|
||||
*neighbor_state == *cell_state / 2 || *neighbor_state == *cell_state * 2
|
||||
}
|
||||
// //! Induces failure if 2 isnt in the set
|
||||
// /// Checks if the constraints between two cells are satisfied.
|
||||
// ///
|
||||
// /// # Arguments
|
||||
// ///
|
||||
// /// * `cell_state` - The state of the current cell.
|
||||
// /// * `neighbor_state` - The state of the neighboring cell.
|
||||
// ///
|
||||
// /// # Returns
|
||||
// ///
|
||||
// /// `true` if the constraints are satisfied, `false` otherwise.
|
||||
//// fn is_constraint_satisfied(cell_state: &Tile, neighbor_state: &Tile) -> bool {
|
||||
// // Check if the neighbor's state is either one more or one less than the current cell's state
|
||||
//// let basic_constraint = *neighbor_state == *cell_state + 1 || *neighbor_state == *cell_state - 1;
|
||||
// // Additional constraint: if the neighbor is 1, the current cell must be 2, and vice versa
|
||||
//// let specific_constraint = (*neighbor_state == 1 && *cell_state == 2) || (*neighbor_state == 2 && *cell_state == 1);
|
||||
//// basic_constraint || specific_constraint
|
||||
//// }
|
||||
|
||||
fn main() {
|
||||
let possible_states = vec![1, 2];
|
||||
let possible_states = vec![1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768];
|
||||
let mut grid = Grid::new(16, 16, possible_states);
|
||||
|
||||
grid.generate();
|
||||
|
|
|
|||
Reference in a new issue