feat: Implemented Inventory.find_free_space

- Added tests for find_free_space
This commit is contained in:
Alexey 2026-03-07 22:00:00 +03:00
commit 5f59e02788
3 changed files with 120 additions and 1 deletions

View file

@ -1,3 +1,5 @@
use std::mem::swap;
use bevy::prelude::*; use bevy::prelude::*;
#[derive(Component, Clone)] #[derive(Component, Clone)]
@ -32,4 +34,16 @@ impl Item {
!rect.intersect(other_rect).is_empty() !rect.intersect(other_rect).is_empty()
} }
/// Swap size.x with size.y
pub fn swap_size(&mut self) {
swap(&mut self.size.x, &mut self.size.y);
}
/// Get clone of item with swapped size
pub fn clone_swapped(&self) -> Self {
let mut new = self.clone();
new.swap_size();
new
}
} }

View file

@ -53,4 +53,39 @@ impl Inventory {
.collect::<Vec<Entity>>(); .collect::<Vec<Entity>>();
self.can_fit(item_query, children.as_slice(), *size, queried_position) self.can_fit(item_query, children.as_slice(), *size, queried_position)
} }
fn find_free_space_inner(
&self,
item_query: Query<&item::Item>,
contained_items: &[Entity],
queried_size: UVec2,
was_swapped: bool,
) -> Option<(UVec2, bool)> {
let Some(UVec2 {x: tries_x, y: tries_y}) = self.size.checked_sub(queried_size) else {
return None;
};
for x in 0..=tries_x {
for y in 0..=tries_y {
let tested_pos = UVec2::new(x, y);
if self.can_fit(item_query, contained_items, queried_size, tested_pos) {
return Some((tested_pos, was_swapped));
}
}
}
None
}
pub fn find_free_space(
&self,
item_query: Query<&item::Item>,
contained_items: &[Entity],
queried_size: UVec2,
) -> Option<(UVec2, bool)> {
let result = self.find_free_space_inner(item_query, contained_items, queried_size, false);
if result.is_some() {
return result;
}
let swapped_size = UVec2::new(queried_size.y, queried_size.x);
self.find_free_space_inner(item_query, contained_items, swapped_size, true)
}
} }

View file

@ -84,30 +84,37 @@ mod inventory {
Inventory::new(UVec2::splat(4)) Inventory::new(UVec2::splat(4))
} }
/// 1x2 0;0
fn item_a() -> Item { fn item_a() -> Item {
Item::new_positioned(UVec2::new(1, 2), UVec2::new(0, 0)) Item::new_positioned(UVec2::new(1, 2), UVec2::new(0, 0))
} }
/// 2x2 0;2
fn item_b() -> Item { fn item_b() -> Item {
Item::new_positioned(UVec2::new(2, 2), UVec2::new(0, 2)) Item::new_positioned(UVec2::new(2, 2), UVec2::new(0, 2))
} }
/// 3x2 1;0
fn item_c() -> Item { fn item_c() -> Item {
Item::new_positioned(UVec2::new(3, 2), UVec2::new(1, 0)) Item::new_positioned(UVec2::new(3, 2), UVec2::new(1, 0))
} }
/// 2x2 2;2
fn item_d() -> Item { fn item_d() -> Item {
Item::new_positioned(UVec2::new(2, 2), UVec2::new(2, 2)) Item::new_positioned(UVec2::new(2, 2), UVec2::new(2, 2))
} }
/// 1x1 0;0
fn item_e() -> Item { fn item_e() -> Item {
Item::new_positioned(UVec2::new(1, 1), UVec2::new(0, 0)) Item::new_positioned(UVec2::new(1, 1), UVec2::new(0, 0))
} }
/// 5x5 0;0
fn item_f() -> Item { fn item_f() -> Item {
Item::new_positioned(UVec2::new(5, 5), UVec2::new(0, 0)) Item::new_positioned(UVec2::new(5, 5), UVec2::new(0, 0))
} }
/// 2x2 3;3
fn item_g() -> Item { fn item_g() -> Item {
Item::new_positioned(UVec2::new(2, 2), UVec2::new(3, 3)) Item::new_positioned(UVec2::new(2, 2), UVec2::new(3, 3))
} }
@ -115,7 +122,7 @@ mod inventory {
#[derive(Resource)] #[derive(Resource)]
struct Items(Vec<Item>, usize, bool); struct Items(Vec<Item>, usize, bool);
#[derive(Component)] #[derive(Component, Resource)]
struct MovableItem(UVec2, bool); struct MovableItem(UVec2, bool);
fn insert_item( fn insert_item(
@ -154,6 +161,20 @@ mod inventory {
} }
} }
fn find_space(
item_query: Query<&Item>,
inventory_query: Query<(&Inventory, &Children)>,
movable_item: Res<MovableItem>,
) {
let (inventory, children) = inventory_query.single().unwrap();
let MovableItem(query_size, assertion) = *movable_item;
if assertion {
assert!(inventory.find_free_space(item_query, children, query_size).is_some());
} else {
assert!(inventory.find_free_space(item_query, children, query_size).is_none());
}
}
#[test] #[test]
fn everything_fits() { fn everything_fits() {
let mut world = World::new(); let mut world = World::new();
@ -230,4 +251,53 @@ mod inventory {
world.run_system(system).expect("Error on running system"); world.run_system(system).expect("Error on running system");
} }
#[test]
fn should_find_space_for_item() {
let mut world = World::new();
let system = world.register_system(find_space);
world.insert_resource(MovableItem(UVec2::new(2, 2), true));
world.spawn(inventory())
.with_child(item_a())
.with_child(item_b())
.with_child(item_c());
world.run_system(system).expect("Error on running system");
}
#[test]
fn should_not_find_space_for_item() {
let mut world = World::new();
let system = world.register_system(find_space);
world.insert_resource(MovableItem(UVec2::new(2, 2), false));
world.spawn(inventory())
.with_child(item_a())
.with_child(item_b())
.with_child(item_c())
.with_child(item_d());
world.run_system(system).expect("Error on running system");
}
#[test]
fn item_fits_if_rotated() {
let mut world = World::new();
let system = world.register_system(find_space);
world.insert_resource(MovableItem(UVec2::new(2, 3), true));
world.spawn(inventory())
.with_child(item_a())
.with_child(item_b())
.with_child(item_d());
world.run_system(system).expect("Error on running system");
}
} }