grid_engine/
grid_engine.rs

1// Copyright (c) 2025 Thiago Ramos
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Grid Engine manages a 2D grid system with support for adding, removing, and moving items.
22//!
23//! # Key Features
24//!
25//! - Automatic collision detection and handling
26//! - Event system for tracking changes
27//! - Expanding the grid on the y axis dynamically
28//!
29//! # Example
30//!
31//! ```
32//! use grid_engine::grid_engine::GridEngine;
33//! # use std::error::Error;
34//!
35//! # fn main() -> Result<(), Box<dyn Error>> {
36//! let mut grid = GridEngine::new(10, 12);
37//!
38//! // Add items to the grid
39//! grid.add_item("item1".to_string(), 2, 2, 2, 4)?;
40//!
41//! // Move items (handles collisions automatically)
42//! grid.move_item("item1", 4, 4)?;
43//!
44//! // Remove items
45//! grid.remove_item("item1")?;
46//! #
47//! # Ok(())
48//! # }
49//! ```
50
51use crate::error::{GridEngineError, InnerGridError, ItemError};
52use crate::grid_events::{ChangesEventValue, GridEvents};
53use crate::inner_grid::{InnerGrid, UpdateGridOperation};
54use crate::node::Node;
55use crate::utils::{ForCellArgs, for_cell};
56use std::{collections::BTreeMap, fmt::Debug};
57
58/// Represents data for an item addition change
59#[derive(Clone, Debug, Eq, PartialEq, Hash)]
60pub struct AddChangeData {
61    /// The node being added to the grid
62    value: Node,
63}
64
65impl AddChangeData {
66    /// Creates a new AddChangeData instance
67    ///
68    /// # Arguments
69    ///
70    /// * `value` - The node being added to the grid
71    ///
72    /// # Returns
73    ///
74    /// A new instance of AddChangeData
75    pub fn new(value: Node) -> Self {
76        Self { value }
77    }
78
79    /// Returns the node being added
80    pub fn value(&self) -> &Node {
81        &self.value
82    }
83}
84
85/// Represents data for an item removal change
86#[derive(Clone, Debug, Eq, PartialEq, Hash)]
87pub struct RemoveChangeData {
88    /// The node being removed from the grid
89    value: Node,
90}
91
92impl RemoveChangeData {
93    /// Creates a new RemoveChangeData instance
94    ///
95    /// # Arguments
96    ///
97    /// * `value` - The node being removed from the grid
98    ///
99    /// # Returns
100    ///
101    /// A new instance of RemoveChangeData
102    pub fn new(value: Node) -> Self {
103        Self { value }
104    }
105
106    /// Returns the node being removed
107    pub fn value(&self) -> &Node {
108        &self.value
109    }
110}
111
112/// Represents data for an item movement change
113#[derive(Clone, Debug, Eq, PartialEq, Hash)]
114pub struct MoveChangeData {
115    /// The original state of the node
116    old_value: Node,
117    /// The new state of the node after movement
118    new_value: Node,
119}
120
121impl MoveChangeData {
122    /// Creates a new MoveChangeData instance
123    ///
124    /// # Arguments
125    ///
126    /// * `old_value` - The original state of the node
127    /// * `new_value` - The new state of the node after movement
128    ///
129    /// # Returns
130    ///
131    /// A new instance of MoveChangeData
132    pub fn new(old_value: Node, new_value: Node) -> Self {
133        Self {
134            old_value,
135            new_value,
136        }
137    }
138
139    /// Returns the original state of the node
140    pub fn old_value(&self) -> &Node {
141        &self.old_value
142    }
143
144    /// Returns the new state of the node after movement
145    pub fn new_value(&self) -> &Node {
146        &self.new_value
147    }
148}
149
150/// Represents different types of changes that can occur in the grid
151#[derive(Clone, Debug, Eq, PartialEq, Hash)]
152pub enum Change {
153    /// Adding a new item to the grid
154    Add(AddChangeData),
155    /// Removing an existing item from the grid
156    Remove(RemoveChangeData),
157    /// Moving an item to a new position
158    Move(MoveChangeData),
159}
160
161/// The main engine for managing a 2D grid system.
162///
163/// `GridEngine` provides functionality for:
164/// - Adding items to specific grid positions
165/// - Moving items while handling collisions
166/// - Removing items from the grid
167/// - Tracking changes through an event system
168/// - Expand the grid dynamically on the y axis
169///
170/// When items collide during placement or movement, the engine automatically
171/// repositions affected items to prevent overlapping, the default is to move the collided items down, increasing their y axis.
172#[derive(Debug)]
173pub struct GridEngine {
174    /// The underlying grid structure
175    grid: InnerGrid,
176    /// Map of item IDs to their Node representations
177    items: BTreeMap<String, Node>,
178    /// Changes waiting to be applied
179    pending_changes: Vec<Change>,
180    /// Event system for tracking grid changes
181    events: GridEvents,
182}
183
184impl GridEngine {
185    /// Creates a new GridEngine with specified dimensions.
186    ///
187    /// # Arguments
188    ///
189    /// * `rows` - Initial number of rows in the grid
190    /// * `cols` - Initial number of columns in the grid
191    ///
192    /// # Example
193    ///
194    /// ```
195    /// use grid_engine::grid_engine::GridEngine;
196    ///
197    /// let grid = GridEngine::new(10, 10); // Creates a 10x10 grid
198    /// ```
199    pub fn new(rows: usize, cols: usize) -> GridEngine {
200        GridEngine {
201            grid: InnerGrid::new(rows, cols),
202            items: BTreeMap::new(),
203            pending_changes: Vec::new(),
204            events: GridEvents::default(),
205        }
206    }
207
208    /// Creates a new node with the specified parameters.
209    fn new_node(&mut self, id: String, x: usize, y: usize, w: usize, h: usize) -> Node {
210        Node::new(id, x, y, w, h)
211    }
212
213    /// Creates a change operation to add a new node to the grid.
214    fn create_add_change(&mut self, node: Node) {
215        self.pending_changes
216            .push(Change::Add(AddChangeData { value: node }));
217    }
218
219    /// Get the nodes sorted by id
220    ///
221    /// # Example
222    ///
223    /// ```
224    /// use grid_engine::grid_engine::GridEngine;
225    ///
226    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
227    /// let mut grid = GridEngine::new(10, 10);
228    /// grid.add_item("b".to_string(), 0, 0, 2, 2).unwrap();
229    /// grid.add_item("a".to_string(), 0, 2, 2, 2).unwrap();
230    ///
231    /// let nodes = grid.get_nodes();
232    /// assert_eq!(nodes.len(), 2);
233    /// assert_eq!(nodes[0].id(), "a");
234    /// assert_eq!(nodes[1].id(), "b");
235    /// # Ok(())
236    /// # }
237    /// ```
238    pub fn get_nodes(&self) -> Vec<Node> {
239        let mut cloned: Vec<Node> = self.items.values().cloned().collect();
240        // Would be better to sort by some created_at
241        cloned.sort_by_key(|n| n.id.clone());
242        cloned
243    }
244
245    /// Gets a reference to the underlying grid structure.
246    ///
247    /// This provides access to the raw grid data for inspection purposes.
248    /// Note that modifications should be made through GridEngine's public methods
249    /// rather than directly manipulating the inner grid.
250    ///
251    /// # Returns
252    ///
253    /// A reference to the InnerGrid instance
254    ///
255    /// # Example
256    ///
257    /// ```
258    /// use grid_engine::grid_engine::GridEngine;
259    /// use std::error::Error;
260    ///
261    /// # fn main() -> Result<(), Box<dyn Error>> {
262    /// let grid = GridEngine::new(10, 10);
263    /// let inner_grid = grid.get_inner_grid();
264    /// assert_eq!(inner_grid.rows(), 10);
265    /// assert_eq!(inner_grid.cols(), 10);
266    /// # Ok(())
267    /// # }
268    pub fn get_inner_grid(&self) -> &InnerGrid {
269        &self.grid
270    }
271
272    /// Adds an item to the grid at the specified position.
273    ///
274    /// If the new item would collide with existing items, those items are
275    /// automatically repositioned to avoid overlap.
276    ///
277    /// # Arguments
278    ///
279    /// * `id` - Unique identifier for the item
280    /// * `x` - X coordinate (column) for item placement
281    /// * `y` - Y coordinate (row) for item placement
282    /// * `w` - Width of the item in grid cells
283    /// * `h` - Height of the item in grid cells
284    ///
285    /// # Returns
286    ///
287    /// * `Ok(&Node)` - Reference to the newly added node
288    /// * `Err(GridEngineError)` - If item already exists or placement fails
289    ///
290    /// # Example
291    ///
292    /// ```
293    /// use grid_engine::grid_engine::GridEngine;
294    /// use std::error::Error;
295    ///
296    /// # fn main() -> Result<(), Box<dyn Error>> {
297    /// let mut grid = GridEngine::new(10, 10);
298    /// grid.add_item("box1".to_string(), 0, 0, 2, 2)?; // 2x2 item at top-left
299    ///
300    /// // Check if the item was added correctly
301    /// let item = grid.get_nodes();
302    /// assert_eq!(item.len(), 1);
303    /// assert_eq!(item[0].id(), "box1");
304    ///
305    /// # Ok(())
306    /// # }
307    /// ```
308    pub fn add_item(
309        &mut self,
310        id: String,
311        x: usize,
312        y: usize,
313        w: usize,
314        h: usize,
315    ) -> Result<&Node, GridEngineError> {
316        if self.items.contains_key(&id) {
317            return Err(GridEngineError::Item(ItemError::ItemAlreadyExists {
318                id: id.clone(),
319            }));
320        };
321
322        let node = self.new_node(id, x, y, w, h);
323        let node_id = node.id.to_string();
324
325        self.handle_collision(&node, x, y, &mut self.grid.clone())?;
326
327        self.create_add_change(node);
328
329        self.apply_changes(&self.pending_changes.clone())?;
330        self.pending_changes.clear();
331
332        let node = self
333            .items
334            .get(&node_id)
335            .ok_or(InnerGridError::MismatchedGridItem { id: node_id })?;
336        Ok(node)
337    }
338
339    fn create_remove_change(&mut self, node: &Node) {
340        self.pending_changes.push(Change::Remove(RemoveChangeData {
341            value: node.clone(),
342        }));
343    }
344
345    /// Removes an item from the grid by its ID.
346    ///
347    /// # Arguments
348    ///
349    /// * `id` - ID of the item to remove
350    ///
351    /// # Returns
352    ///
353    /// * `Ok(Node)` - The removed node
354    /// * `Err(GridEngineError)` - If item doesn't exist
355    ///
356    /// # Example
357    ///
358    /// ```
359    /// use grid_engine::grid_engine::GridEngine;
360    /// use std::error::Error;
361    ///
362    /// # fn main() -> Result<(), Box<dyn Error>> {
363    ///
364    /// let mut grid = GridEngine::new(10, 10);
365    /// grid.add_item("box1".to_string(), 0, 0, 2, 2)?;
366    /// grid.remove_item("box1")?; // Removes the item
367    ///
368    /// # Ok(())
369    /// # }
370    /// ```
371    pub fn remove_item(&mut self, id: &str) -> Result<Node, GridEngineError> {
372        let node = match self.items.get(id) {
373            Some(node) => node,
374            None => Err(GridEngineError::Item(ItemError::ItemNotFound {
375                id: id.to_string(),
376            }))?,
377        }
378        .clone();
379
380        self.create_remove_change(&node);
381
382        self.apply_changes(&self.pending_changes.clone())?;
383        self.pending_changes.clear();
384        Ok(node)
385    }
386
387    /// Checks if a node would collide with any existing items at the specified position.
388    ///
389    /// This is used internally to detect potential collisions before making grid changes.
390    /// It considers the node's dimensions and any existing items in the target area.
391    ///
392    /// # Returns
393    ///
394    /// * `Ok(Vec<&Node>)` - List of nodes that would collide with the given node
395    /// * `Err(InnerGridError)` - If position check fails (e.g., out of bounds)
396    fn will_collides_with(
397        &self,
398        node: &Node,
399        x: usize,
400        y: usize,
401        grid: &mut InnerGrid,
402    ) -> Result<Vec<&Node>, InnerGridError> {
403        let mut collides_with: Vec<&Node> = Vec::new();
404
405        for_cell(
406            ForCellArgs {
407                x,
408                y,
409                w: node.w,
410                h: node.h,
411            },
412            &mut |x, y| {
413                let cell = grid
414                    .get(x, y)
415                    .ok_or(InnerGridError::OutOfBoundsAccess { x, y })?;
416
417                match cell {
418                    Some(cell_ref) => {
419                        if cell_ref != &node.id {
420                            let node = self.items.get(cell_ref).ok_or(
421                                InnerGridError::MismatchedGridItem {
422                                    id: cell_ref.to_string(),
423                                },
424                            )?;
425
426                            if !collides_with.contains(&node) {
427                                collides_with.push(node);
428                            }
429                        }
430                    }
431                    None => {
432                        // Nothing to collide with
433                    }
434                }
435                Ok(())
436            },
437        )?;
438
439        Ok(collides_with)
440    }
441
442    /// Handles collision resolution when adding or moving items.
443    ///
444    /// When a collision is detected, this method:
445    /// 1. Identifies all affected items
446    /// 2. Calculates new positions for colliding items
447    /// 3. Creates appropriate move changes to relocate affected items
448    ///
449    /// The default collision resolution strategy moves affected items downward,
450    /// which may trigger dynamic grid expansion in the y-axis.
451    fn handle_collision(
452        &mut self,
453        node: &Node,
454        x: usize,
455        y: usize,
456        grid: &mut InnerGrid,
457    ) -> Result<(), InnerGridError> {
458        let collides_with = self
459            .will_collides_with(node, x, y, grid)?
460            .iter()
461            .map(|n| (*n).clone())
462            .collect::<Vec<Node>>();
463
464        for collided in collides_with {
465            let mut new_grid = grid.clone();
466
467            node.update_grid(&mut new_grid, UpdateGridOperation::Remove)?;
468            let new_x = collided.x;
469            let new_y = y + node.h;
470            self.create_move_change(collided, new_x, new_y, &mut new_grid)?;
471        }
472
473        Ok(())
474    }
475
476    /// Creates a change operation to move a node to a new position.
477    ///
478    /// This method:
479    /// 1. Handles any collisions at the new position
480    /// 2. Checks if the node was already scheduled to move
481    /// 3. Creates a Move change operation if needed
482    ///
483    /// # Arguments
484    ///
485    /// * `node` - The node to move
486    /// * `new_x` - Target x coordinate
487    /// * `new_y` - Target y coordinate
488    /// * `grid` - The grid to check for collisions
489    fn create_move_change(
490        &mut self,
491        node: Node,
492        new_x: usize,
493        new_y: usize,
494        grid: &mut InnerGrid,
495    ) -> Result<(), InnerGridError> {
496        let old_node = node.clone();
497        self.handle_collision(&node, new_x, new_y, grid)?;
498
499        let already_moved = self.pending_changes.iter().any(|change| match change {
500            Change::Move(data) => data.new_value.id == node.id,
501            _ => false,
502        });
503
504        if already_moved {
505            return Ok(());
506        }
507
508        self.pending_changes.push(Change::Move(MoveChangeData {
509            old_value: old_node,
510            new_value: Node::new(node.id.to_string(), new_x, new_y, node.w, node.h),
511        }));
512
513        Ok(())
514    }
515
516    /// Moves an existing item to a new position in the grid.
517    ///
518    /// If the move would cause collisions, affected items are automatically
519    /// repositioned to prevent overlap.
520    ///
521    /// # Arguments
522    ///
523    /// * `id` - ID of the item to move
524    /// * `new_x` - New X coordinate
525    /// * `new_y` - New Y coordinate
526    ///
527    /// # Returns
528    ///
529    /// * `Ok(())` - If move successful
530    /// * `Err(GridEngineError)` - If item doesn't exist or move invalid
531    ///
532    /// # Example
533    ///
534    /// ```
535    /// use grid_engine::grid_engine::GridEngine;
536    /// # use std::error::Error;
537    ///
538    /// # fn main() -> Result<(), Box<dyn Error>> {
539    ///
540    /// let mut grid = GridEngine::new(10, 10);
541    /// grid.add_item("box1".to_string(), 0, 0, 2, 2)?;
542    /// grid.move_item("box1", 2, 2)?; // Moves box to position 2,2
543    ///
544    /// // Check if the item was moved correctly
545    /// let item = grid.get_nodes();
546    /// assert_eq!(item.len(), 1);
547    /// assert_eq!(item[0].x(), &2);
548    /// assert_eq!(item[0].y(), &2);
549    ///
550    /// # Ok(())
551    /// # }
552    ///
553    /// ```
554    pub fn move_item(
555        &mut self,
556        id: &str,
557        new_x: usize,
558        new_y: usize,
559    ) -> Result<(), GridEngineError> {
560        let node = match self.items.get(id) {
561            Some(node) => node,
562            None => Err(GridEngineError::Item(ItemError::ItemNotFound {
563                id: id.to_string(),
564            }))?,
565        };
566
567        self.create_move_change(node.clone(), new_x, new_y, &mut self.grid.clone())?;
568
569        self.apply_changes(&self.pending_changes.clone())?;
570        self.pending_changes.clear();
571
572        Ok(())
573    }
574
575    /// Applies a batch of changes to the grid.
576    ///
577    /// This method handles the actual application of all pending changes to both
578    /// the grid structure and the item tracking system. Changes are applied in order,
579    /// and all operations are executed atomically - if any change fails, none of
580    /// the changes will be applied.
581    ///
582    /// After successful application, triggers change events to notify any registered listeners.
583    ///
584    /// # Arguments
585    ///
586    /// * `changes` - Vector of changes to apply (Add, Remove, or Move operations)
587    ///
588    /// # Returns
589    ///
590    /// * `Ok(())` - If all changes were applied successfully
591    /// * `Err(GridEngineError)` - If any change application fails
592    /// ```
593    fn apply_changes(&mut self, changes: &[Change]) -> Result<(), GridEngineError> {
594        for change in changes.iter() {
595            match &change {
596                Change::Add(data) => {
597                    let node = &data.value;
598
599                    node.update_grid(&mut self.grid, UpdateGridOperation::Add)?;
600
601                    self.items.insert(node.id.to_string(), node.clone());
602                }
603                Change::Remove(data) => {
604                    let node = &data.value;
605
606                    node.update_grid(&mut self.grid, UpdateGridOperation::Remove)?;
607
608                    self.items.remove(&node.id);
609                }
610                Change::Move(data) => {
611                    let node = &data.new_value;
612                    let old_node = &data.old_value;
613
614                    old_node.update_grid(&mut self.grid, UpdateGridOperation::Remove)?;
615
616                    self.items.insert(node.id.to_string(), node.clone());
617
618                    node.update_grid(&mut self.grid, UpdateGridOperation::Add)?;
619                }
620            }
621        }
622
623        self.events
624            .trigger_changes_event(&ChangesEventValue::new(changes.to_vec()));
625        Ok(())
626    }
627
628    /// Returns a reference to the grid events system.
629    pub fn events(&self) -> &GridEvents {
630        &self.events
631    }
632
633    /// Returns a mutable reference to the grid events system.
634    pub fn events_mut(&mut self) -> &mut GridEvents {
635        &mut self.events
636    }
637}
638
639mod tests {
640    #[allow(unused_imports)]
641    use super::*;
642
643    #[test]
644    fn test_for_cell() {
645        let mut results = Vec::new();
646        let mut callback = |x: usize, y: usize| {
647            results.push((x, y));
648            Ok(())
649        };
650
651        for_cell(
652            ForCellArgs {
653                x: 1,
654                y: 2,
655                w: 2,
656                h: 2,
657            },
658            &mut callback,
659        )
660        .unwrap();
661
662        assert_eq!(results, vec![(1, 2), (1, 3), (2, 2), (2, 3)]);
663    }
664
665    #[test]
666    fn test_add_item() {
667        let mut engine = GridEngine::new(10, 10);
668        let item_0_id = engine
669            .add_item("0".to_string(), 0, 0, 2, 2)
670            .unwrap()
671            .id
672            .clone();
673
674        assert!(engine.items.len() == 1);
675        for_cell(
676            ForCellArgs {
677                x: 0,
678                y: 0,
679                w: 2,
680                h: 2,
681            },
682            &mut |x, y| {
683                assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_0_id);
684                Ok(())
685            },
686        )
687        .unwrap();
688    }
689
690    #[test]
691    fn test_add_item_handle_duplicated_id() {
692        let mut engine = GridEngine::new(10, 10);
693        engine.add_item("0".to_string(), 0, 0, 2, 2).unwrap();
694
695        assert!(engine.add_item("0".to_string(), 0, 0, 2, 2).is_err())
696    }
697
698    #[test]
699    fn test_add_item_handle_collision() {
700        let mut engine = GridEngine::new(10, 10);
701        let item_0_id = engine
702            .add_item("0".to_string(), 0, 0, 2, 2)
703            .unwrap()
704            .id
705            .clone();
706        let item_1_id = engine
707            .add_item("1".to_string(), 0, 0, 2, 2)
708            .unwrap()
709            .id
710            .clone();
711
712        // Item 0 should stay in position 0, 0
713        let item_0 = engine.items.get(&item_0_id).unwrap();
714        assert_eq!(item_0.x, 0);
715        assert_eq!(item_0.y, 2);
716        item_0
717            .for_cell(&mut |x, y| {
718                assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_0_id);
719                Ok(())
720            })
721            .unwrap();
722
723        // Item 1 should go to position 0, 2
724        let item_1 = engine.items.get(&item_1_id).unwrap();
725        assert_eq!(item_1.x, 0);
726        assert_eq!(item_1.y, 0);
727        item_1
728            .for_cell(&mut |x, y| {
729                assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_1_id);
730                Ok(())
731            })
732            .unwrap();
733    }
734
735    #[test]
736    fn test_remove_item() {
737        let mut engine = GridEngine::new(10, 10);
738        let item_0_id = engine
739            .add_item("0".to_string(), 0, 0, 2, 3)
740            .unwrap()
741            .id
742            .clone();
743        engine.remove_item(&item_0_id).unwrap();
744        for_cell(
745            ForCellArgs {
746                x: 0,
747                y: 0,
748                w: 2,
749                h: 3,
750            },
751            &mut |x, y| {
752                let value = engine.grid.get(x, y).unwrap();
753                assert_eq!(value, &None);
754                Ok(())
755            },
756        )
757        .unwrap();
758    }
759
760    #[test]
761    fn test_move_item() {
762        let mut engine = GridEngine::new(10, 10);
763        let item_0_id = engine
764            .add_item("0".to_string(), 0, 0, 2, 2)
765            .unwrap()
766            .id
767            .clone();
768        engine.move_item(&item_0_id, 1, 1).unwrap();
769
770        // Asserts that its present on the new position
771        for_cell(
772            ForCellArgs {
773                x: 1,
774                y: 1,
775                w: 2,
776                h: 2,
777            },
778            &mut |x, y| {
779                let item_on_expected_position = engine.grid.get(x, y).unwrap().as_ref().unwrap();
780                assert_eq!(item_on_expected_position, &item_0_id);
781                Ok(())
782            },
783        )
784        .unwrap();
785
786        // Asserts that its not present on the old position
787        for_cell(
788            ForCellArgs {
789                x: 0,
790                y: 0,
791                w: 1,
792                h: 1,
793            },
794            &mut |x, y| {
795                assert_eq!(engine.grid.get(x, y).unwrap(), &None);
796                Ok(())
797            },
798        )
799        .unwrap();
800    }
801
802    #[test]
803    fn test_move_item_handle_collision() {
804        let mut engine = GridEngine::new(10, 10);
805        let item_0_id = engine
806            .add_item("0".to_string(), 0, 0, 2, 2)
807            .unwrap()
808            .id
809            .clone();
810        let item_1_id = engine
811            .add_item("1".to_string(), 0, 2, 2, 2)
812            .unwrap()
813            .id
814            .clone();
815        engine.move_item("0", 0, 1).unwrap();
816
817        // Item 0 should go to position 0, 1
818        let item_0 = engine.items.get(&item_0_id).unwrap();
819        assert_eq!(item_0.x, 0);
820        assert_eq!(item_0.y, 1);
821        item_0
822            .for_cell(&mut |x, y| {
823                assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_0_id);
824                Ok(())
825            })
826            .unwrap();
827
828        // Item 1 should go to position 0, 3
829        let item_1 = engine.items.get(&item_1_id).unwrap();
830        assert_eq!(item_1.x, 0);
831        assert_eq!(item_1.y, 3);
832        item_1
833            .for_cell(&mut |x, y| {
834                assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_1_id);
835                Ok(())
836            })
837            .unwrap();
838    }
839
840    #[test]
841    fn test_will_collides_with() {
842        let mut engine = GridEngine::new(10, 10);
843        let item_0_id = engine
844            .add_item("0".to_string(), 0, 0, 1, 2)
845            .unwrap()
846            .id
847            .clone();
848
849        // Asserts that does not collide with self
850        assert!(
851            engine
852                .will_collides_with(
853                    engine.items.get(&item_0_id).unwrap(),
854                    0,
855                    0,
856                    &mut engine.grid.clone()
857                )
858                .unwrap()
859                .is_empty()
860        );
861
862        // Asserts that does not collide with empty position
863        assert!(
864            engine
865                .will_collides_with(
866                    engine.items.get(&item_0_id).unwrap(),
867                    2,
868                    2,
869                    &mut engine.grid.clone()
870                )
871                .unwrap()
872                .is_empty()
873        );
874
875        // Asserts that collide with occupied position
876        engine.add_item("1".to_string(), 1, 2, 1, 2).unwrap();
877
878        // Full collision
879        assert!(
880            engine
881                .will_collides_with(
882                    engine.items.get(&item_0_id).unwrap(),
883                    1,
884                    2,
885                    &mut engine.grid.clone()
886                )
887                .unwrap()
888                .len()
889                == 1
890        );
891
892        // Partial collision
893        assert!(
894            engine
895                .will_collides_with(
896                    engine.items.get(&item_0_id).unwrap(),
897                    1,
898                    1,
899                    &mut engine.grid.clone()
900                )
901                .unwrap()
902                .len()
903                == 1
904        );
905    }
906
907    #[test]
908    fn test_get_nodes() {
909        let mut engine = GridEngine::new(10, 10);
910        let item_0_id = engine
911            .add_item("0".to_string(), 0, 0, 2, 2)
912            .unwrap()
913            .id
914            .clone();
915        let item_1_id = engine
916            .add_item("1".to_string(), 0, 2, 2, 2)
917            .unwrap()
918            .id
919            .clone();
920
921        let nodes = engine.get_nodes();
922        assert_eq!(nodes.len(), 2);
923        assert_eq!(nodes[0].id, item_0_id);
924        assert_eq!(nodes[1].id, item_1_id);
925    }
926
927    #[test]
928    fn test_move_result_will_not_collides_with_moving_item() {
929        let mut engine = GridEngine::new(10, 10);
930        engine.add_item("0".to_string(), 0, 0, 2, 3).unwrap();
931        engine.add_item("1".to_string(), 0, 6, 2, 2).unwrap();
932        engine.move_item("1", 0, 2).unwrap();
933
934        for_cell(
935            ForCellArgs {
936                x: 0,
937                y: 7,
938                w: 2,
939                h: 2,
940            },
941            &mut |x, y| {
942                let value = engine.grid.get(x, y).unwrap();
943                println!("value: {:?}", value);
944                assert_ne!(value, &Some("1".to_string()));
945                Ok(())
946            },
947        )
948        .unwrap();
949    }
950
951    #[test]
952    fn test_node_movements_that_collides_twice_works() {
953        let mut engine = GridEngine::new(14, 10);
954        engine.add_item("0".to_string(), 1, 1, 2, 3).unwrap();
955        engine.add_item("1".to_string(), 2, 4, 2, 4).unwrap();
956        engine.add_item("2".to_string(), 0, 6, 2, 4).unwrap();
957        engine.move_item("2", 1, 2).unwrap();
958
959        println!("Items: {:#?}", engine.items);
960
961        engine.items.iter().for_each(|(_, node)| {
962            node.for_cell(&mut |x, y| {
963                let value = engine.grid.get(x, y).unwrap();
964                println!("Validating x: {}, y: {}", x, y);
965                assert_eq!(&Some(node.clone().id), value);
966                Ok(())
967            })
968            .unwrap();
969        });
970    }
971}