use crate::error::{GridEngineError, InnerGridError, ItemError};
use crate::grid_events::{ChangesEventValue, GridEvents};
use crate::inner_grid::{InnerGrid, UpdateGridOperation};
use crate::node::Node;
use crate::utils::{ForCellArgs, for_cell};
use std::{collections::BTreeMap, fmt::Debug};
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct AddChangeData {
value: Node,
}
impl AddChangeData {
pub fn new(value: Node) -> Self {
Self { value }
}
pub fn value(&self) -> &Node {
&self.value
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct RemoveChangeData {
value: Node,
}
impl RemoveChangeData {
pub fn new(value: Node) -> Self {
Self { value }
}
pub fn value(&self) -> &Node {
&self.value
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct MoveChangeData {
old_value: Node,
new_value: Node,
}
impl MoveChangeData {
pub fn new(old_value: Node, new_value: Node) -> Self {
Self {
old_value,
new_value,
}
}
pub fn old_value(&self) -> &Node {
&self.old_value
}
pub fn new_value(&self) -> &Node {
&self.new_value
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Change {
Add(AddChangeData),
Remove(RemoveChangeData),
Move(MoveChangeData),
}
#[derive(Debug)]
pub struct GridEngine {
grid: InnerGrid,
items: BTreeMap<String, Node>,
pending_changes: Vec<Change>,
events: GridEvents,
}
impl GridEngine {
pub fn new(rows: usize, cols: usize) -> GridEngine {
GridEngine {
grid: InnerGrid::new(rows, cols),
items: BTreeMap::new(),
pending_changes: Vec::new(),
events: GridEvents::default(),
}
}
fn new_node(&mut self, id: impl Into<String>, x: usize, y: usize, w: usize, h: usize) -> Node {
Node::new(id.into(), x, y, w, h)
}
fn create_add_change(&mut self, node: Node) {
self.pending_changes
.push(Change::Add(AddChangeData { value: node }));
}
pub fn get_nodes(&self) -> Vec<&Node> {
let mut cloned: Vec<&Node> = self.items.values().collect();
cloned.sort_by_key(|n| n.id.clone());
cloned
}
pub fn get_inner_grid(&self) -> &InnerGrid {
&self.grid
}
pub fn add_item(
&mut self,
id: impl Into<String>,
x: usize,
y: usize,
w: usize,
h: usize,
) -> Result<&Node, GridEngineError> {
let id = id.into();
if self.items.contains_key(&id) {
return Err(GridEngineError::Item(ItemError::ItemAlreadyExists { id }));
};
let node = self.new_node(id, x, y, w, h);
let node_id = node.id.to_string();
self.handle_collision(&node, x, y, &mut self.grid.clone())?;
self.create_add_change(node);
self.apply_changes(&self.pending_changes.clone())?;
self.pending_changes.clear();
let node = self
.items
.get(&node_id)
.ok_or(InnerGridError::MismatchedGridItem { id: node_id })?;
Ok(node)
}
fn create_remove_change(&mut self, node: &Node) {
self.pending_changes.push(Change::Remove(RemoveChangeData {
value: node.clone(),
}));
}
pub fn remove_item(&mut self, id: &str) -> Result<Node, GridEngineError> {
let node = match self.items.get(id) {
Some(node) => node,
None => Err(GridEngineError::Item(ItemError::ItemNotFound {
id: id.to_string(),
}))?,
}
.clone();
self.create_remove_change(&node);
self.apply_changes(&self.pending_changes.clone())?;
self.pending_changes.clear();
Ok(node)
}
fn will_collides_with(
&self,
node: &Node,
x: usize,
y: usize,
grid: &mut InnerGrid,
) -> Result<Vec<&Node>, InnerGridError> {
let mut collides_with: Vec<&Node> = Vec::new();
for_cell(
ForCellArgs {
x,
y,
w: node.w,
h: node.h,
},
&mut |x, y| {
let cell = grid
.get(x, y)
.ok_or(InnerGridError::OutOfBoundsAccess { x, y })?;
match cell {
Some(cell_ref) => {
if cell_ref != &node.id {
let node = self.items.get(cell_ref).ok_or(
InnerGridError::MismatchedGridItem {
id: cell_ref.to_string(),
},
)?;
if !collides_with.contains(&node) {
collides_with.push(node);
}
}
}
None => {
}
}
Ok(())
},
)?;
Ok(collides_with)
}
fn handle_collision(
&mut self,
node: &Node,
x: usize,
y: usize,
grid: &mut InnerGrid,
) -> Result<(), InnerGridError> {
let collides_with = self
.will_collides_with(node, x, y, grid)?
.iter()
.map(|n| (*n).clone())
.collect::<Vec<Node>>();
for collided in collides_with {
let mut new_grid = grid.clone();
node.update_grid(&mut new_grid, UpdateGridOperation::Remove)?;
let new_x = collided.x;
let new_y = y + node.h;
self.create_move_change(collided, new_x, new_y, &mut new_grid)?;
}
Ok(())
}
fn create_move_change(
&mut self,
node: Node,
new_x: usize,
new_y: usize,
grid: &mut InnerGrid,
) -> Result<(), InnerGridError> {
let old_node = node.clone();
self.handle_collision(&node, new_x, new_y, grid)?;
let already_moved = self.pending_changes.iter().any(|change| match change {
Change::Move(data) => data.new_value.id == node.id,
_ => false,
});
if already_moved {
return Ok(());
}
self.pending_changes.push(Change::Move(MoveChangeData {
old_value: old_node,
new_value: Node::new(node.id.to_string(), new_x, new_y, node.w, node.h),
}));
Ok(())
}
pub fn move_item(
&mut self,
id: &str,
new_x: usize,
new_y: usize,
) -> Result<(), GridEngineError> {
let node = match self.items.get(id) {
Some(node) => node,
None => Err(GridEngineError::Item(ItemError::ItemNotFound {
id: id.to_string(),
}))?,
};
self.create_move_change(node.clone(), new_x, new_y, &mut self.grid.clone())?;
self.apply_changes(&self.pending_changes.clone())?;
self.pending_changes.clear();
Ok(())
}
fn apply_changes(&mut self, changes: &[Change]) -> Result<(), GridEngineError> {
for change in changes.iter() {
match &change {
Change::Add(data) => {
let node = &data.value;
node.update_grid(&mut self.grid, UpdateGridOperation::Add)?;
self.items.insert(node.id.to_string(), node.clone());
}
Change::Remove(data) => {
let node = &data.value;
node.update_grid(&mut self.grid, UpdateGridOperation::Remove)?;
self.items.remove(&node.id);
}
Change::Move(data) => {
let node = &data.new_value;
let old_node = &data.old_value;
old_node.update_grid(&mut self.grid, UpdateGridOperation::Remove)?;
self.items.insert(node.id.to_string(), node.clone());
node.update_grid(&mut self.grid, UpdateGridOperation::Add)?;
}
}
}
self.events
.trigger_changes_event(&ChangesEventValue::new(changes.to_vec()));
Ok(())
}
pub fn events(&self) -> &GridEvents {
&self.events
}
pub fn events_mut(&mut self) -> &mut GridEvents {
&mut self.events
}
}
mod tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_for_cell() {
let mut results = Vec::new();
let mut callback = |x: usize, y: usize| {
results.push((x, y));
Ok(())
};
for_cell(
ForCellArgs {
x: 1,
y: 2,
w: 2,
h: 2,
},
&mut callback,
)
.unwrap();
assert_eq!(results, vec![(1, 2), (1, 3), (2, 2), (2, 3)]);
}
#[test]
fn test_add_item() {
let mut engine = GridEngine::new(10, 10);
let item_0_id = engine
.add_item("0".to_string(), 0, 0, 2, 2)
.unwrap()
.id
.clone();
assert!(engine.items.len() == 1);
for_cell(
ForCellArgs {
x: 0,
y: 0,
w: 2,
h: 2,
},
&mut |x, y| {
assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_0_id);
Ok(())
},
)
.unwrap();
}
#[test]
fn test_add_item_handle_duplicated_id() {
let mut engine = GridEngine::new(10, 10);
engine.add_item("0".to_string(), 0, 0, 2, 2).unwrap();
assert!(engine.add_item("0".to_string(), 0, 0, 2, 2).is_err())
}
#[test]
fn test_add_item_handle_collision() {
let mut engine = GridEngine::new(10, 10);
let item_0_id = engine
.add_item("0".to_string(), 0, 0, 2, 2)
.unwrap()
.id
.clone();
let item_1_id = engine
.add_item("1".to_string(), 0, 0, 2, 2)
.unwrap()
.id
.clone();
let item_0 = engine.items.get(&item_0_id).unwrap();
assert_eq!(item_0.x, 0);
assert_eq!(item_0.y, 2);
item_0
.for_cell(&mut |x, y| {
assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_0_id);
Ok(())
})
.unwrap();
let item_1 = engine.items.get(&item_1_id).unwrap();
assert_eq!(item_1.x, 0);
assert_eq!(item_1.y, 0);
item_1
.for_cell(&mut |x, y| {
assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_1_id);
Ok(())
})
.unwrap();
}
#[test]
fn test_remove_item() {
let mut engine = GridEngine::new(10, 10);
let item_0_id = engine
.add_item("0".to_string(), 0, 0, 2, 3)
.unwrap()
.id
.clone();
engine.remove_item(&item_0_id).unwrap();
for_cell(
ForCellArgs {
x: 0,
y: 0,
w: 2,
h: 3,
},
&mut |x, y| {
let value = engine.grid.get(x, y).unwrap();
assert_eq!(value, &None);
Ok(())
},
)
.unwrap();
}
#[test]
fn test_move_item() {
let mut engine = GridEngine::new(10, 10);
let item_0_id = engine
.add_item("0".to_string(), 0, 0, 2, 2)
.unwrap()
.id
.clone();
engine.move_item(&item_0_id, 1, 1).unwrap();
for_cell(
ForCellArgs {
x: 1,
y: 1,
w: 2,
h: 2,
},
&mut |x, y| {
let item_on_expected_position = engine.grid.get(x, y).unwrap().as_ref().unwrap();
assert_eq!(item_on_expected_position, &item_0_id);
Ok(())
},
)
.unwrap();
for_cell(
ForCellArgs {
x: 0,
y: 0,
w: 1,
h: 1,
},
&mut |x, y| {
assert_eq!(engine.grid.get(x, y).unwrap(), &None);
Ok(())
},
)
.unwrap();
}
#[test]
fn test_move_item_handle_collision() {
let mut engine = GridEngine::new(10, 10);
let item_0_id = engine
.add_item("0".to_string(), 0, 0, 2, 2)
.unwrap()
.id
.clone();
let item_1_id = engine
.add_item("1".to_string(), 0, 2, 2, 2)
.unwrap()
.id
.clone();
engine.move_item("0", 0, 1).unwrap();
let item_0 = engine.items.get(&item_0_id).unwrap();
assert_eq!(item_0.x, 0);
assert_eq!(item_0.y, 1);
item_0
.for_cell(&mut |x, y| {
assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_0_id);
Ok(())
})
.unwrap();
let item_1 = engine.items.get(&item_1_id).unwrap();
assert_eq!(item_1.x, 0);
assert_eq!(item_1.y, 3);
item_1
.for_cell(&mut |x, y| {
assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_1_id);
Ok(())
})
.unwrap();
}
#[test]
fn test_will_collides_with() {
let mut engine = GridEngine::new(10, 10);
let item_0_id = engine
.add_item("0".to_string(), 0, 0, 1, 2)
.unwrap()
.id
.clone();
assert!(
engine
.will_collides_with(
engine.items.get(&item_0_id).unwrap(),
0,
0,
&mut engine.grid.clone()
)
.unwrap()
.is_empty()
);
assert!(
engine
.will_collides_with(
engine.items.get(&item_0_id).unwrap(),
2,
2,
&mut engine.grid.clone()
)
.unwrap()
.is_empty()
);
engine.add_item("1".to_string(), 1, 2, 1, 2).unwrap();
assert!(
engine
.will_collides_with(
engine.items.get(&item_0_id).unwrap(),
1,
2,
&mut engine.grid.clone()
)
.unwrap()
.len()
== 1
);
assert!(
engine
.will_collides_with(
engine.items.get(&item_0_id).unwrap(),
1,
1,
&mut engine.grid.clone()
)
.unwrap()
.len()
== 1
);
}
#[test]
fn test_get_nodes() {
let mut engine = GridEngine::new(10, 10);
let item_0_id = engine
.add_item("0".to_string(), 0, 0, 2, 2)
.unwrap()
.id
.clone();
let item_1_id = engine
.add_item("1".to_string(), 0, 2, 2, 2)
.unwrap()
.id
.clone();
let nodes = engine.get_nodes();
assert_eq!(nodes.len(), 2);
assert_eq!(nodes[0].id, item_0_id);
assert_eq!(nodes[1].id, item_1_id);
}
#[test]
fn test_move_result_will_not_collides_with_moving_item() {
let mut engine = GridEngine::new(10, 10);
engine.add_item("0".to_string(), 0, 0, 2, 3).unwrap();
engine.add_item("1".to_string(), 0, 6, 2, 2).unwrap();
engine.move_item("1", 0, 2).unwrap();
for_cell(
ForCellArgs {
x: 0,
y: 7,
w: 2,
h: 2,
},
&mut |x, y| {
let value = engine.grid.get(x, y).unwrap();
println!("value: {:?}", value);
assert_ne!(value, &Some("1".to_string()));
Ok(())
},
)
.unwrap();
}
#[test]
fn test_node_movements_that_collides_twice_works() {
let mut engine = GridEngine::new(14, 10);
engine.add_item("0".to_string(), 1, 1, 2, 3).unwrap();
engine.add_item("1".to_string(), 2, 4, 2, 4).unwrap();
engine.add_item("2".to_string(), 0, 6, 2, 4).unwrap();
engine.move_item("2", 1, 2).unwrap();
println!("Items: {:#?}", engine.items);
engine.items.iter().for_each(|(_, node)| {
node.for_cell(&mut |x, y| {
let value = engine.grid.get(x, y).unwrap();
println!("Validating x: {}, y: {}", x, y);
assert_eq!(&Some(node.clone().id), value);
Ok(())
})
.unwrap();
});
}
}