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: impl Into<String>, x: usize, y: usize, w: usize, h: usize) -> Node {
210        Node::new(id.into(), 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 node 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().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: impl Into<String>,
311        x: usize,
312        y: usize,
313        w: usize,
314        h: usize,
315    ) -> Result<&Node, GridEngineError> {
316        let id = id.into();
317        if self.items.contains_key(&id) {
318            return Err(GridEngineError::Item(ItemError::ItemAlreadyExists { id }));
319        };
320
321        let node = self.new_node(id, x, y, w, h);
322        let node_id = node.id.to_string();
323
324        self.handle_collision(&node, x, y, &mut self.grid.clone())?;
325
326        self.create_add_change(node);
327
328        self.apply_changes(&self.pending_changes.clone())?;
329        self.pending_changes.clear();
330
331        let node = self
332            .items
333            .get(&node_id)
334            .ok_or(InnerGridError::MismatchedGridItem { id: node_id })?;
335        Ok(node)
336    }
337
338    fn create_remove_change(&mut self, node: &Node) {
339        self.pending_changes.push(Change::Remove(RemoveChangeData {
340            value: node.clone(),
341        }));
342    }
343
344    /// Removes an item from the grid by its ID.
345    ///
346    /// # Arguments
347    ///
348    /// * `id` - ID of the item to remove
349    ///
350    /// # Returns
351    ///
352    /// * `Ok(Node)` - The removed node
353    /// * `Err(GridEngineError)` - If item doesn't exist
354    ///
355    /// # Example
356    ///
357    /// ```
358    /// use grid_engine::grid_engine::GridEngine;
359    /// use std::error::Error;
360    ///
361    /// # fn main() -> Result<(), Box<dyn Error>> {
362    ///
363    /// let mut grid = GridEngine::new(10, 10);
364    /// grid.add_item("box1".to_string(), 0, 0, 2, 2)?;
365    /// grid.remove_item("box1")?; // Removes the item
366    ///
367    /// # Ok(())
368    /// # }
369    /// ```
370    pub fn remove_item(&mut self, id: &str) -> Result<Node, GridEngineError> {
371        let node = match self.items.get(id) {
372            Some(node) => node,
373            None => Err(GridEngineError::Item(ItemError::ItemNotFound {
374                id: id.to_string(),
375            }))?,
376        }
377        .clone();
378
379        self.create_remove_change(&node);
380
381        self.apply_changes(&self.pending_changes.clone())?;
382        self.pending_changes.clear();
383        Ok(node)
384    }
385
386    /// Checks if a node would collide with any existing items at the specified position.
387    ///
388    /// This is used internally to detect potential collisions before making grid changes.
389    /// It considers the node's dimensions and any existing items in the target area.
390    ///
391    /// # Returns
392    ///
393    /// * `Ok(Vec<&Node>)` - List of nodes that would collide with the given node
394    /// * `Err(InnerGridError)` - If position check fails (e.g., out of bounds)
395    fn will_collides_with(
396        &self,
397        node: &Node,
398        x: usize,
399        y: usize,
400        grid: &mut InnerGrid,
401    ) -> Result<Vec<&Node>, InnerGridError> {
402        let mut collides_with: Vec<&Node> = Vec::new();
403
404        for_cell(
405            ForCellArgs {
406                x,
407                y,
408                w: node.w,
409                h: node.h,
410            },
411            &mut |x, y| {
412                let cell = grid
413                    .get(x, y)
414                    .ok_or(InnerGridError::OutOfBoundsAccess { x, y })?;
415
416                match cell {
417                    Some(cell_ref) => {
418                        if cell_ref != &node.id {
419                            let node = self.items.get(cell_ref).ok_or(
420                                InnerGridError::MismatchedGridItem {
421                                    id: cell_ref.to_string(),
422                                },
423                            )?;
424
425                            if !collides_with.contains(&node) {
426                                collides_with.push(node);
427                            }
428                        }
429                    }
430                    None => {
431                        // Nothing to collide with
432                    }
433                }
434                Ok(())
435            },
436        )?;
437
438        Ok(collides_with)
439    }
440
441    /// Handles collision resolution when adding or moving items.
442    ///
443    /// When a collision is detected, this method:
444    /// 1. Identifies all affected items
445    /// 2. Calculates new positions for colliding items
446    /// 3. Creates appropriate move changes to relocate affected items
447    ///
448    /// The default collision resolution strategy moves affected items downward,
449    /// which may trigger dynamic grid expansion in the y-axis.
450    fn handle_collision(
451        &mut self,
452        node: &Node,
453        x: usize,
454        y: usize,
455        grid: &mut InnerGrid,
456    ) -> Result<(), InnerGridError> {
457        let collides_with = self
458            .will_collides_with(node, x, y, grid)?
459            .iter()
460            .map(|n| (*n).clone())
461            .collect::<Vec<Node>>();
462
463        for collided in collides_with {
464            let mut new_grid = grid.clone();
465
466            node.update_grid(&mut new_grid, UpdateGridOperation::Remove)?;
467            let new_x = collided.x;
468            let new_y = y + node.h;
469            self.create_move_change(collided, new_x, new_y, &mut new_grid)?;
470        }
471
472        Ok(())
473    }
474
475    /// Creates a change operation to move a node to a new position.
476    ///
477    /// This method:
478    /// 1. Handles any collisions at the new position
479    /// 2. Checks if the node was already scheduled to move
480    /// 3. Creates a Move change operation if needed
481    ///
482    /// # Arguments
483    ///
484    /// * `node` - The node to move
485    /// * `new_x` - Target x coordinate
486    /// * `new_y` - Target y coordinate
487    /// * `grid` - The grid to check for collisions
488    fn create_move_change(
489        &mut self,
490        node: Node,
491        new_x: usize,
492        new_y: usize,
493        grid: &mut InnerGrid,
494    ) -> Result<(), InnerGridError> {
495        let old_node = node.clone();
496        self.handle_collision(&node, new_x, new_y, grid)?;
497
498        let already_moved = self.pending_changes.iter().any(|change| match change {
499            Change::Move(data) => data.new_value.id == node.id,
500            _ => false,
501        });
502
503        if already_moved {
504            return Ok(());
505        }
506
507        self.pending_changes.push(Change::Move(MoveChangeData {
508            old_value: old_node,
509            new_value: Node::new(node.id.to_string(), new_x, new_y, node.w, node.h),
510        }));
511
512        Ok(())
513    }
514
515    /// Moves an existing item to a new position in the grid.
516    ///
517    /// If the move would cause collisions, affected items are automatically
518    /// repositioned to prevent overlap.
519    ///
520    /// # Arguments
521    ///
522    /// * `id` - ID of the item to move
523    /// * `new_x` - New X coordinate
524    /// * `new_y` - New Y coordinate
525    ///
526    /// # Returns
527    ///
528    /// * `Ok(())` - If move successful
529    /// * `Err(GridEngineError)` - If item doesn't exist or move invalid
530    ///
531    /// # Example
532    ///
533    /// ```
534    /// use grid_engine::grid_engine::GridEngine;
535    /// # use std::error::Error;
536    ///
537    /// # fn main() -> Result<(), Box<dyn Error>> {
538    ///
539    /// let mut grid = GridEngine::new(10, 10);
540    /// grid.add_item("box1".to_string(), 0, 0, 2, 2)?;
541    /// grid.move_item("box1", 2, 2)?; // Moves box to position 2,2
542    ///
543    /// // Check if the item was moved correctly
544    /// let item = grid.get_nodes();
545    /// assert_eq!(item.len(), 1);
546    /// assert_eq!(item[0].x(), &2);
547    /// assert_eq!(item[0].y(), &2);
548    ///
549    /// # Ok(())
550    /// # }
551    ///
552    /// ```
553    pub fn move_item(
554        &mut self,
555        id: &str,
556        new_x: usize,
557        new_y: usize,
558    ) -> Result<(), GridEngineError> {
559        let node = match self.items.get(id) {
560            Some(node) => node,
561            None => Err(GridEngineError::Item(ItemError::ItemNotFound {
562                id: id.to_string(),
563            }))?,
564        };
565
566        self.create_move_change(node.clone(), new_x, new_y, &mut self.grid.clone())?;
567
568        self.apply_changes(&self.pending_changes.clone())?;
569        self.pending_changes.clear();
570
571        Ok(())
572    }
573
574    /// Applies a batch of changes to the grid.
575    ///
576    /// This method handles the actual application of all pending changes to both
577    /// the grid structure and the item tracking system. Changes are applied in order,
578    /// and all operations are executed atomically - if any change fails, none of
579    /// the changes will be applied.
580    ///
581    /// After successful application, triggers change events to notify any registered listeners.
582    ///
583    /// # Arguments
584    ///
585    /// * `changes` - Vector of changes to apply (Add, Remove, or Move operations)
586    ///
587    /// # Returns
588    ///
589    /// * `Ok(())` - If all changes were applied successfully
590    /// * `Err(GridEngineError)` - If any change application fails
591    /// ```
592    fn apply_changes(&mut self, changes: &[Change]) -> Result<(), GridEngineError> {
593        for change in changes.iter() {
594            match &change {
595                Change::Add(data) => {
596                    let node = &data.value;
597
598                    node.update_grid(&mut self.grid, UpdateGridOperation::Add)?;
599
600                    self.items.insert(node.id.to_string(), node.clone());
601                }
602                Change::Remove(data) => {
603                    let node = &data.value;
604
605                    node.update_grid(&mut self.grid, UpdateGridOperation::Remove)?;
606
607                    self.items.remove(&node.id);
608                }
609                Change::Move(data) => {
610                    let node = &data.new_value;
611                    let old_node = &data.old_value;
612
613                    old_node.update_grid(&mut self.grid, UpdateGridOperation::Remove)?;
614
615                    self.items.insert(node.id.to_string(), node.clone());
616
617                    node.update_grid(&mut self.grid, UpdateGridOperation::Add)?;
618                }
619            }
620        }
621
622        self.events
623            .trigger_changes_event(&ChangesEventValue::new(changes.to_vec()));
624        Ok(())
625    }
626
627    /// Returns a reference to the grid events system.
628    pub fn events(&self) -> &GridEvents {
629        &self.events
630    }
631
632    /// Returns a mutable reference to the grid events system.
633    pub fn events_mut(&mut self) -> &mut GridEvents {
634        &mut self.events
635    }
636}
637
638mod tests {
639    #[allow(unused_imports)]
640    use super::*;
641
642    #[test]
643    fn test_for_cell() {
644        let mut results = Vec::new();
645        let mut callback = |x: usize, y: usize| {
646            results.push((x, y));
647            Ok(())
648        };
649
650        for_cell(
651            ForCellArgs {
652                x: 1,
653                y: 2,
654                w: 2,
655                h: 2,
656            },
657            &mut callback,
658        )
659        .unwrap();
660
661        assert_eq!(results, vec![(1, 2), (1, 3), (2, 2), (2, 3)]);
662    }
663
664    #[test]
665    fn test_add_item() {
666        let mut engine = GridEngine::new(10, 10);
667        let item_0_id = engine
668            .add_item("0".to_string(), 0, 0, 2, 2)
669            .unwrap()
670            .id
671            .clone();
672
673        assert!(engine.items.len() == 1);
674        for_cell(
675            ForCellArgs {
676                x: 0,
677                y: 0,
678                w: 2,
679                h: 2,
680            },
681            &mut |x, y| {
682                assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_0_id);
683                Ok(())
684            },
685        )
686        .unwrap();
687    }
688
689    #[test]
690    fn test_add_item_handle_duplicated_id() {
691        let mut engine = GridEngine::new(10, 10);
692        engine.add_item("0".to_string(), 0, 0, 2, 2).unwrap();
693
694        assert!(engine.add_item("0".to_string(), 0, 0, 2, 2).is_err())
695    }
696
697    #[test]
698    fn test_add_item_handle_collision() {
699        let mut engine = GridEngine::new(10, 10);
700        let item_0_id = engine
701            .add_item("0".to_string(), 0, 0, 2, 2)
702            .unwrap()
703            .id
704            .clone();
705        let item_1_id = engine
706            .add_item("1".to_string(), 0, 0, 2, 2)
707            .unwrap()
708            .id
709            .clone();
710
711        // Item 0 should stay in position 0, 0
712        let item_0 = engine.items.get(&item_0_id).unwrap();
713        assert_eq!(item_0.x, 0);
714        assert_eq!(item_0.y, 2);
715        item_0
716            .for_cell(&mut |x, y| {
717                assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_0_id);
718                Ok(())
719            })
720            .unwrap();
721
722        // Item 1 should go to position 0, 2
723        let item_1 = engine.items.get(&item_1_id).unwrap();
724        assert_eq!(item_1.x, 0);
725        assert_eq!(item_1.y, 0);
726        item_1
727            .for_cell(&mut |x, y| {
728                assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_1_id);
729                Ok(())
730            })
731            .unwrap();
732    }
733
734    #[test]
735    fn test_remove_item() {
736        let mut engine = GridEngine::new(10, 10);
737        let item_0_id = engine
738            .add_item("0".to_string(), 0, 0, 2, 3)
739            .unwrap()
740            .id
741            .clone();
742        engine.remove_item(&item_0_id).unwrap();
743        for_cell(
744            ForCellArgs {
745                x: 0,
746                y: 0,
747                w: 2,
748                h: 3,
749            },
750            &mut |x, y| {
751                let value = engine.grid.get(x, y).unwrap();
752                assert_eq!(value, &None);
753                Ok(())
754            },
755        )
756        .unwrap();
757    }
758
759    #[test]
760    fn test_move_item() {
761        let mut engine = GridEngine::new(10, 10);
762        let item_0_id = engine
763            .add_item("0".to_string(), 0, 0, 2, 2)
764            .unwrap()
765            .id
766            .clone();
767        engine.move_item(&item_0_id, 1, 1).unwrap();
768
769        // Asserts that its present on the new position
770        for_cell(
771            ForCellArgs {
772                x: 1,
773                y: 1,
774                w: 2,
775                h: 2,
776            },
777            &mut |x, y| {
778                let item_on_expected_position = engine.grid.get(x, y).unwrap().as_ref().unwrap();
779                assert_eq!(item_on_expected_position, &item_0_id);
780                Ok(())
781            },
782        )
783        .unwrap();
784
785        // Asserts that its not present on the old position
786        for_cell(
787            ForCellArgs {
788                x: 0,
789                y: 0,
790                w: 1,
791                h: 1,
792            },
793            &mut |x, y| {
794                assert_eq!(engine.grid.get(x, y).unwrap(), &None);
795                Ok(())
796            },
797        )
798        .unwrap();
799    }
800
801    #[test]
802    fn test_move_item_handle_collision() {
803        let mut engine = GridEngine::new(10, 10);
804        let item_0_id = engine
805            .add_item("0".to_string(), 0, 0, 2, 2)
806            .unwrap()
807            .id
808            .clone();
809        let item_1_id = engine
810            .add_item("1".to_string(), 0, 2, 2, 2)
811            .unwrap()
812            .id
813            .clone();
814        engine.move_item("0", 0, 1).unwrap();
815
816        // Item 0 should go to position 0, 1
817        let item_0 = engine.items.get(&item_0_id).unwrap();
818        assert_eq!(item_0.x, 0);
819        assert_eq!(item_0.y, 1);
820        item_0
821            .for_cell(&mut |x, y| {
822                assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_0_id);
823                Ok(())
824            })
825            .unwrap();
826
827        // Item 1 should go to position 0, 3
828        let item_1 = engine.items.get(&item_1_id).unwrap();
829        assert_eq!(item_1.x, 0);
830        assert_eq!(item_1.y, 3);
831        item_1
832            .for_cell(&mut |x, y| {
833                assert_eq!(engine.grid.get(x, y).unwrap().as_ref().unwrap(), &item_1_id);
834                Ok(())
835            })
836            .unwrap();
837    }
838
839    #[test]
840    fn test_will_collides_with() {
841        let mut engine = GridEngine::new(10, 10);
842        let item_0_id = engine
843            .add_item("0".to_string(), 0, 0, 1, 2)
844            .unwrap()
845            .id
846            .clone();
847
848        // Asserts that does not collide with self
849        assert!(
850            engine
851                .will_collides_with(
852                    engine.items.get(&item_0_id).unwrap(),
853                    0,
854                    0,
855                    &mut engine.grid.clone()
856                )
857                .unwrap()
858                .is_empty()
859        );
860
861        // Asserts that does not collide with empty position
862        assert!(
863            engine
864                .will_collides_with(
865                    engine.items.get(&item_0_id).unwrap(),
866                    2,
867                    2,
868                    &mut engine.grid.clone()
869                )
870                .unwrap()
871                .is_empty()
872        );
873
874        // Asserts that collide with occupied position
875        engine.add_item("1".to_string(), 1, 2, 1, 2).unwrap();
876
877        // Full collision
878        assert!(
879            engine
880                .will_collides_with(
881                    engine.items.get(&item_0_id).unwrap(),
882                    1,
883                    2,
884                    &mut engine.grid.clone()
885                )
886                .unwrap()
887                .len()
888                == 1
889        );
890
891        // Partial collision
892        assert!(
893            engine
894                .will_collides_with(
895                    engine.items.get(&item_0_id).unwrap(),
896                    1,
897                    1,
898                    &mut engine.grid.clone()
899                )
900                .unwrap()
901                .len()
902                == 1
903        );
904    }
905
906    #[test]
907    fn test_get_nodes() {
908        let mut engine = GridEngine::new(10, 10);
909        let item_0_id = engine
910            .add_item("0".to_string(), 0, 0, 2, 2)
911            .unwrap()
912            .id
913            .clone();
914        let item_1_id = engine
915            .add_item("1".to_string(), 0, 2, 2, 2)
916            .unwrap()
917            .id
918            .clone();
919
920        let nodes = engine.get_nodes();
921        assert_eq!(nodes.len(), 2);
922        assert_eq!(nodes[0].id, item_0_id);
923        assert_eq!(nodes[1].id, item_1_id);
924    }
925
926    #[test]
927    fn test_move_result_will_not_collides_with_moving_item() {
928        let mut engine = GridEngine::new(10, 10);
929        engine.add_item("0".to_string(), 0, 0, 2, 3).unwrap();
930        engine.add_item("1".to_string(), 0, 6, 2, 2).unwrap();
931        engine.move_item("1", 0, 2).unwrap();
932
933        for_cell(
934            ForCellArgs {
935                x: 0,
936                y: 7,
937                w: 2,
938                h: 2,
939            },
940            &mut |x, y| {
941                let value = engine.grid.get(x, y).unwrap();
942                println!("value: {:?}", value);
943                assert_ne!(value, &Some("1".to_string()));
944                Ok(())
945            },
946        )
947        .unwrap();
948    }
949
950    #[test]
951    fn test_node_movements_that_collides_twice_works() {
952        let mut engine = GridEngine::new(14, 10);
953        engine.add_item("0".to_string(), 1, 1, 2, 3).unwrap();
954        engine.add_item("1".to_string(), 2, 4, 2, 4).unwrap();
955        engine.add_item("2".to_string(), 0, 6, 2, 4).unwrap();
956        engine.move_item("2", 1, 2).unwrap();
957
958        println!("Items: {:#?}", engine.items);
959
960        engine.items.iter().for_each(|(_, node)| {
961            node.for_cell(&mut |x, y| {
962                let value = engine.grid.get(x, y).unwrap();
963                println!("Validating x: {}, y: {}", x, y);
964                assert_eq!(&Some(node.clone().id), value);
965                Ok(())
966            })
967            .unwrap();
968        });
969    }
970}