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
189
src/main.rs
189
src/main.rs
|
|
@ -1,9 +1,38 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use rand::{distributions::{Distribution, WeightedIndex}, rngs::ThreadRng, thread_rng};
|
use rand::{distributions::{Distribution, WeightedIndex}, rngs::ThreadRng, thread_rng};
|
||||||
|
|
||||||
|
/// Represents a tile in the grid.
|
||||||
type Tile = i32;
|
type Tile = i32;
|
||||||
|
|
||||||
|
/// A collection of possible states a cell can be in.
|
||||||
type PossibleStates = Vec<Tile>;
|
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)]
|
#[derive(Clone)]
|
||||||
struct Cell {
|
struct Cell {
|
||||||
possible_states: PossibleStates,
|
possible_states: PossibleStates,
|
||||||
|
|
@ -12,6 +41,15 @@ struct Cell {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
fn new(possible_states: PossibleStates) -> Self {
|
||||||
let entropy = calculate_entropy(&possible_states);
|
let entropy = calculate_entropy(&possible_states);
|
||||||
Cell {
|
Cell {
|
||||||
|
|
@ -22,26 +60,39 @@ impl Cell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information about a cell, specifically for use in a min-heap.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct CellInfo {
|
struct CellInfo {
|
||||||
entropy: f64,
|
entropy: f64,
|
||||||
coordinates: (usize, usize),
|
coordinates: (usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A min-heap data structure for managing cells based on their entropy.
|
||||||
struct MinHeap {
|
struct MinHeap {
|
||||||
data: Vec<CellInfo>,
|
data: Vec<CellInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MinHeap {
|
impl MinHeap {
|
||||||
|
/// Creates a new, empty `MinHeap`.
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
MinHeap { data: Vec::new() }
|
MinHeap { data: Vec::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a `CellInfo` element to the heap.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `element` - The `CellInfo` to be added.
|
||||||
fn add(&mut self, element: CellInfo) {
|
fn add(&mut self, element: CellInfo) {
|
||||||
self.data.push(element);
|
self.data.push(element);
|
||||||
self.heapify_up(self.data.len() - 1);
|
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> {
|
fn pop(&mut self) -> Option<CellInfo> {
|
||||||
if self.data.is_empty() {
|
if self.data.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -53,6 +104,11 @@ impl MinHeap {
|
||||||
result
|
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) {
|
fn heapify_up(&mut self, mut index: usize) {
|
||||||
while index != 0 {
|
while index != 0 {
|
||||||
let parent_index = (index - 1) / 2;
|
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) {
|
fn heapify_down(&mut self, mut index: usize) {
|
||||||
let length = self.data.len();
|
let length = self.data.len();
|
||||||
loop {
|
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 {
|
fn calculate_entropy(data: &[Tile]) -> f64 {
|
||||||
let mut frequency_map = HashMap::new();
|
let mut frequency_map = HashMap::new();
|
||||||
for &value in data {
|
for &value in data {
|
||||||
|
|
@ -103,16 +173,29 @@ fn calculate_entropy(data: &[Tile]) -> f64 {
|
||||||
entropy
|
entropy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the entire grid of cells.
|
||||||
struct Grid {
|
struct Grid {
|
||||||
cells: Vec<Vec<Cell>>,
|
cells: Vec<Vec<Cell>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Grid {
|
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 {
|
fn new(width: usize, height: usize, possible_states: PossibleStates) -> Self {
|
||||||
let cells = vec![vec![Cell::new(possible_states.clone()); width]; height];
|
let cells = vec![vec![Cell::new(possible_states.clone()); width]; height];
|
||||||
Grid { cells }
|
Grid { cells }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Observes the grid and collapses cells based on their states and neighbors.
|
||||||
fn observe_and_collapse(&mut self) {
|
fn observe_and_collapse(&mut self) {
|
||||||
let mut rng = thread_rng();
|
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)> {
|
fn find_min_entropy_cell(&self) -> Option<(usize, usize)> {
|
||||||
let mut heap = MinHeap::new();
|
let mut heap = MinHeap::new();
|
||||||
|
|
||||||
|
|
@ -156,6 +244,13 @@ impl Grid {
|
||||||
heap.pop().map(|cell_info| cell_info.coordinates)
|
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) {
|
fn collapse_cell(&mut self, x: usize, y: usize, rng: &mut ThreadRng) {
|
||||||
let cell = &mut self.cells[y][x];
|
let cell = &mut self.cells[y][x];
|
||||||
|
|
||||||
|
|
@ -172,37 +267,54 @@ impl Grid {
|
||||||
cell.collapsed = true;
|
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 {
|
fn updated_possible_states_based_on_neighbors(&self, x: usize, y: usize, current_states: &PossibleStates) -> PossibleStates {
|
||||||
let mut neighbors = Vec::new();
|
let mut neighbors = Vec::new();
|
||||||
|
|
||||||
if y > 0 { neighbors.push(&self.cells[y - 1][x]); }
|
// Assuming self.cells is a 2D grid of cells
|
||||||
if y < self.cells.len() - 1 { neighbors.push(&self.cells[y + 1][x]); }
|
// Collect the states of neighboring cells if they are collapsed
|
||||||
if x > 0 { neighbors.push(&self.cells[y][x - 1]); }
|
if y > 0 && self.cells[y - 1][x].collapsed {
|
||||||
if x < self.cells[0].len() - 1 { neighbors.push(&self.cells[y][x + 1]); }
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
let filtered_states: PossibleStates = current_states.iter()
|
// Filter the states based on the constraints with the neighbors
|
||||||
.filter(|&&state| {
|
current_states.iter().cloned().filter(|&state| {
|
||||||
neighbors.iter().all(|neighbor| {
|
neighbors.iter().all(|&neighbor_state| {
|
||||||
!neighbor.collapsed || (neighbor.collapsed && neighbor.possible_states != vec![state])
|
is_constraint_satisfied(&state, &neighbor_state)
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.cloned()
|
}).collect()
|
||||||
.collect();
|
|
||||||
|
|
||||||
// println!("Cell[{}, {}] - Before: {:?}, After: {:?}", x, y, current_states, filtered_states);
|
|
||||||
filtered_states
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Generates the final state of the grid.
|
||||||
fn generate(&mut self) {
|
fn generate(&mut self) {
|
||||||
while self.cells.iter().any(|row| row.iter().any(|cell| !cell.collapsed)) {
|
while self.cells.iter().any(|row| row.iter().any(|cell| !cell.collapsed)) {
|
||||||
self.observe_and_collapse();
|
self.observe_and_collapse();
|
||||||
|
|
||||||
for row in &mut self.cells {
|
for row in &mut self.cells {
|
||||||
for cell in row {
|
for cell in row {
|
||||||
if cell.possible_states.is_empty() && !cell.collapsed {
|
if cell.possible_states.is_empty() && !cell.collapsed {
|
||||||
cell.possible_states = vec![0];
|
cell.possible_states = vec![99999];
|
||||||
cell.collapsed = true;
|
cell.collapsed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -210,21 +322,54 @@ impl Grid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prints the current state of the grid.
|
||||||
fn print_grid(&self) {
|
fn print_grid(&self) {
|
||||||
for row in &self.cells {
|
for row in &self.cells {
|
||||||
for cell in row {
|
for cell in row {
|
||||||
let state = cell.possible_states.get(0).map_or('0', |&s| {
|
let state = cell.possible_states.get(0).map_or(0, |&s| s);
|
||||||
if s == 0 { '0' } else { char::from_digit(s as u32, 10).unwrap_or('?') }
|
print!("{:05} ", state); // Print each state as a 2-digit number with leading zeros if necessary
|
||||||
});
|
|
||||||
print!("{} ", state);
|
|
||||||
}
|
}
|
||||||
println!();
|
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() {
|
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);
|
let mut grid = Grid::new(16, 16, possible_states);
|
||||||
|
|
||||||
grid.generate();
|
grid.generate();
|
||||||
|
|
|
||||||
Reference in a new issue