Skip to content

Commit

Permalink
✨ Add maze generation feature (#90)
Browse files Browse the repository at this point in the history
  • Loading branch information
bal7hazar authored Sep 10, 2024
1 parent 8cbb96d commit 9e1f51b
Show file tree
Hide file tree
Showing 14 changed files with 2,103 additions and 73 deletions.
82 changes: 82 additions & 0 deletions crates/map/src/helpers/asserter.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//! Map assert helper functions.

// Constants
pub const MAX_SIZE: u8 = 252;

/// Errors module.
pub mod errors {
pub const ASSERTER_INVALID_DIMENSION: felt252 = 'Asserter: invalid dimension';
pub const ASSERTER_POSITION_IS_CORNER: felt252 = 'Asserter: position is a corner';
pub const ASSERTER_POSITION_NOT_EDGE: felt252 = 'Asserter: position not an edge';
}

#[generate_trait]
pub impl Asserter of AssertTrait {
/// Check if the position is on the edge of the map.
/// # Arguments
/// * `width` - The width of the map
/// * `height` - The height of the map
/// * `x` - The x coordinate of the position
/// * `y` - The y coordinate of the position
/// # Returns
/// * `true` if the position is on the edge of the map, `false` otherwise
#[inline]
fn is_edge(width: u8, height: u8, x: u8, y: u8) -> bool {
x == 0 || y == 0 || x == width - 1 || y == height - 1
}

/// Check if the position is a corner of the map.
/// # Arguments
/// * `width` - The width of the map
/// * `height` - The height of the map
/// * `x` - The x coordinate of the position
/// * `y` - The y coordinate of the position
/// # Returns
/// * `true` if the position is a corner of the map, `false` otherwise
#[inline]
fn is_corner(width: u8, height: u8, x: u8, y: u8) -> bool {
(x == 0 && y == 0)
|| (x == width - 1 && y == 0)
|| (x == 0 && y == height - 1)
|| (x == width - 1 && y == height - 1)
}

/// Assert that the dimensions are valid.
/// # Arguments
/// * `width` - The width of the map
/// * `height` - The height of the map
/// # Panics
/// * If the dimensions are invalid
#[inline]
fn assert_valid_dimension(width: u8, height: u8) {
assert(width > 2, errors::ASSERTER_INVALID_DIMENSION);
assert(height > 2, errors::ASSERTER_INVALID_DIMENSION);
assert(width * height <= MAX_SIZE, errors::ASSERTER_INVALID_DIMENSION);
}

/// Assert that the position is on the edge of the map.
/// # Arguments
/// * `width` - The width of the map
/// * `height` - The height of the map
/// * `position` - The position to check
/// # Panics
/// * If the position is not on the edge of the map
#[inline]
fn assert_on_edge(width: u8, height: u8, position: u8) {
let (x, y) = (position % width, position / width);
assert(Self::is_edge(width, height, x, y), errors::ASSERTER_POSITION_NOT_EDGE);
}

/// Assert that the position is not a corner of the map.
/// # Arguments
/// * `width` - The width of the map
/// * `height` - The height of the map
/// * `position` - The position to check
/// # Panics
/// * If the position is a corner of the map
#[inline]
fn assert_not_corner(width: u8, height: u8, position: u8) {
let (x, y) = (position % width, position / width);
assert(!Self::is_corner(width, height, x, y), errors::ASSERTER_POSITION_IS_CORNER);
}
}
200 changes: 200 additions & 0 deletions crates/map/src/helpers/bitmap.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// Internal imports

use origami_map::helpers::power::{TwoPower, TwoPowerTrait};

#[generate_trait]
pub impl Bitmap of BitmapTrait {
/// Count the number of bits set to 1 in the number
/// # Arguments
/// * `x` - The value for which to count the number of bits set to 1
/// # Returns
/// * The number of bits set to 1
#[inline]
fn popcount(x: felt252) -> u8 {
let mut x: u256 = x.into();
let mut count: u8 = 0;
while (x > 0) {
count += PrivateTrait::_popcount((x % 0x100000000).try_into().unwrap());
x /= 0x100000000;
};
count
}

/// Get the bit at the specified index
/// # Arguments
/// * `x` - The bitmap
/// * `index` - The index of the bit to get
/// # Returns
/// * The value of the bit at the specified index
#[inline]
fn get(x: felt252, index: u8) -> u8 {
let x: u256 = x.into();
let offset: u256 = TwoPower::pow(index);
(x / offset % 2).try_into().unwrap()
}

/// Set the bit at the specified index
/// # Arguments
/// * `x` - The bitmap
/// * `index` - The index of the bit to set
/// # Returns
/// * The bitmap with the bit at the specified index set to 1
#[inline]
fn set(x: felt252, index: u8) -> felt252 {
let x: u256 = x.into();
let offset: u256 = TwoPower::pow(index);
let bit = x / offset % 2;
let offset: u256 = offset * (1 - bit);
(x + offset).try_into().unwrap()
}

/// Unset the bit at the specified index
/// # Arguments
/// * `x` - The bitmap
/// * `index` - The index of the bit to unset
/// # Returns
/// * The bitmap with the bit at the specified index set to 0
#[inline]
fn unset(x: felt252, index: u8) -> felt252 {
let x: u256 = x.into();
let offset: u256 = TwoPower::pow(index);
let bit = x / offset % 2;
let offset: u256 = offset * bit;
(x - offset).try_into().unwrap()
}

/// The index of the least significant bit of the number,
/// where the least significant bit is at index 0 and the most significant bit is at index 255
/// # Arguments
/// * `x` - The value for which to compute the least significant bit, must be greater than 0.
/// # Returns
/// * The index of the least significant bit, if 0 returns the index 0
#[inline]
fn least_significant_bit(x: felt252) -> u8 {
let mut x: u256 = x.into();
if x == 0 {
return 0;
}
let mut r: u8 = 255;

if (x & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) > 0 {
r -= 128;
} else {
x /= 0x100000000000000000000000000000000;
}
if (x & 0xFFFFFFFFFFFFFFFF) > 0 {
r -= 64;
} else {
x /= 0x10000000000000000;
}
if (x & 0xFFFFFFFF) > 0 {
r -= 32;
} else {
x /= 0x100000000;
}
if (x & 0xFFFF) > 0 {
r -= 16;
} else {
x /= 0x10000;
}
if (x & 0xFF) > 0 {
r -= 8;
} else {
x /= 0x100;
}
if (x & 0xF) > 0 {
r -= 4;
} else {
x /= 0x10;
}
if (x & 0x3) > 0 {
r -= 2;
} else {
x /= 0x4;
}
if (x & 0x1) > 0 {
r -= 1;
}
r
}
}

#[generate_trait]
impl Private of PrivateTrait {
/// Count the number of bits set to 1 in the number for a u32
/// # Arguments
/// * `x` - The value for which to count the number of bits set to 1
/// # Returns
/// * The number of bits set to 1
#[inline]
fn _popcount(mut x: u32) -> u8 {
x -= ((x / 2) & 0x55555555);
x = (x & 0x33333333) + ((x / 4) & 0x33333333);
x = (x + (x / 16)) & 0x0f0f0f0f;
x += (x / 256);
x += (x / 65536);
return (x % 64).try_into().unwrap();
}
}

#[cfg(test)]
mod tests {
// Local imports

use super::Bitmap;

#[test]
fn test_bitmap_popcount_large() {
let count = Bitmap::popcount(0x4003FBB391C53CCB8E99752EB665586B695BB2D026BEC9071FF30002);
assert_eq!(count, 109);
}

#[test]
fn test_bitmap_popcount_small() {
let count = Bitmap::popcount(0b101);
assert_eq!(count, 2);
}

#[test]
fn test_bitmap_get() {
let bit = Bitmap::get(0b1001011, 0);
assert_eq!(bit, 1);
}

#[test]
fn test_bitmap_set() {
let bit = Bitmap::set(0b1001010, 0);
assert_eq!(bit, 0b1001011);
}

#[test]
fn test_bitmap_set_unchanged() {
let bit = Bitmap::set(0b1001011, 0);
assert_eq!(bit, 0b1001011);
}

#[test]
fn test_bitmap_unset() {
let bit = Bitmap::unset(0b1001011, 0);
assert_eq!(bit, 0b1001010);
}

#[test]
fn test_bitmap_unset_unchanged() {
let bit = Bitmap::unset(0b1001010, 0);
assert_eq!(bit, 0b1001010);
}

#[test]
fn test_bitmap_least_significant_bit_null() {
let msb = Bitmap::least_significant_bit(0);
assert!(msb == 0, "Bitmap: least significant bit");
}

#[test]
fn test_bitmap_least_significant_bit() {
let bitmap: felt252 = 1234; // 10011010010
let msb = Bitmap::least_significant_bit(bitmap);
assert!(msb == 1, "Bitmap: least significant bit");
}
}
Loading

0 comments on commit 9e1f51b

Please sign in to comment.