Skip to content

origami

Origami

Origami is a collection of essential primitives designed to facilitate the development of onchain games using the Dojo engine. It provides a set of powerful tools and libraries that enable game developers to create complex, engaging, and secure blockchain-based games.

Each crate focuses on a specific aspect of game development, from mathematical operations and procedural generation to economic mechanisms and security primitives. For advanced mathematical computations that complement these game development primitives, consider also using Alexandria's extended math library.

Installation

Add the crates you need to your Scarb.toml:

[dependencies]
origami_algebra = { git = "https://github.com/dojoengine/origami" }
origami_map = { git = "https://github.com/dojoengine/origami" }
origami_random = { git = "https://github.com/dojoengine/origami" }
# Add others as needed...

Algebra

Mathematical structures and operations for 2D game development including vector operations and algebraic computations.

Key Features: Vec2 operations (dot product, swizzling), vector utilities, matrix operations

// Example: checking if an enemy is within a player's field of view
 
use origami_algebra::vec2::{Vec2, Vec2Trait};
 
#[derive(Copy, Drop, Serde, IntrospectPacked)]
pub struct GameVec2 { pub x: u32, pub y: u32 }
 
#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct Position {
    #[key] pub entity: ContractAddress,
    pub vec: GameVec2, // Where the entity is
}
 
#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct Facing {
    #[key] pub entity: ContractAddress,
    pub direction: GameVec2, // Where the entity is facing
}
 
fn can_see_enemy(world: WorldStorage, player: ContractAddress, enemy: ContractAddress) -> bool {
    let player_pos: Position = world.read_model(player);
    let enemy_pos: Position = world.read_model(enemy);
    let player_facing: Facing = world.read_model(player);
 
    // Calculate direction from player to enemy
    let to_enemy = Vec2Trait::new(
        enemy_pos.vec.x - player_pos.vec.x,
        enemy_pos.vec.y - player_pos.vec.y
    );
 
    let facing_vec = Vec2Trait::new(
        player_facing.direction.x,
        player_facing.direction.y
    );
 
    // If dot > 0, enemy is in front of player (within 180° field of view)
    facing_vec.dot(to_enemy) > 0
}

Map

Tools for generating and manipulating 2D grid-based worlds with procedural generation algorithms and pathfinding.

Key Features: Maze/cave/random walk generation, A* pathfinding, object distribution, hex grids

// Example: creating and exploring a dungeon with hidden treasures
 
use origami_map::map::{Map, MapTrait};
use origami_map::helpers::bitmap::{Bitmap, BitmapTrait};
 
#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct GameWorld {
    #[key] pub game_id: u32,
    pub map: Map,
}
 
#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct Treasure {
    #[key] pub game_id: u32,
    #[key] pub position: u8,
    pub discovered: bool,
}
 
// Create dungeon with entrance and hidden treasures
fn generate_dungeon(ref world: WorldStorage, game_id: u32) {
    let seed = get_block_timestamp().into() + game_id.into();
 
    // Generate closed maze with few branching paths
    let mut map = MapTrait::new_maze(18, 14, 0, seed);
 
    // Add entrance corridor from edge position 63
    map.open_with_corridor(63, 0);
 
    // Distribute 5 treasures randomly across walkable tiles
    let treasure_positions = map.compute_distribution(5, seed);
 
    // Store treasures at computed positions
    let mut pos: u8 = 1;
    while pos < 252 { // 252 = 18 * 14
        if Bitmap::get(treasure_positions, pos) == 1 {
            world.write_model(@Treasure {
                game_id,
                position: pos,
                discovered: false
            });
        }
        pos += 1;
    };
 
    world.write_model(@GameWorld { game_id, map });
}
 
// Find path between two points
fn find_path(world: WorldStorage, game_id: u32, start: u8, end: u8) -> Span<u8> {
    let game_world: GameWorld = world.read_model(game_id);
    game_world.map.search_path(start, end)
}

Random

Pseudo-random generation for unpredictable game mechanics including dice rolling and deck management.

Key Features: Customizable dice, card deck shuffling/drawing, seeded generation

// Example: opening loot boxes with random rarity and item selection
 
use origami_random::dice::{Dice, DiceTrait};
use origami_random::deck::{Deck, DeckTrait};
 
#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct LootBox {
    #[key] pub player: ContractAddress,
    #[key] pub box_id: u32,
    pub rarity: u8,
    pub item_id: u8,
}
 
fn open_loot_box(ref world: WorldStorage, player: ContractAddress, box_id: u32) {
    let seed = get_block_timestamp().into() + box_id.into();
 
    // Roll for rarity and determine loot quality
    let mut rarity_dice = DiceTrait::new(6, seed);
    let rarity = rarity_dice.roll();
    let deck_size = match rarity { 6 => 10, 5 => 20, 4 => 40, _ => 100 };
 
    // Draw items from appropriate deck
    let mut item_deck = DeckTrait::new(seed, deck_size);
    let item_count = if rarity >= 5 { 3 } else if rarity >= 3 { 2 } else { 1 };
 
    let mut i = 0;
    while i < item_count {
        let loot = LootBox {
            player, box_id: box_id + i.into(),
            rarity: rarity.try_into().unwrap(),
            item_id: item_deck.draw().try_into().unwrap(),
        };
        world.write_model(@loot);
        i += 1;
    }
}

DeFi

Sophisticated auction mechanisms for in-game economies using Gradual Dutch Auctions (GDA) and Variable Rate GDAs (VRGDA).

Key Features: Discrete/continuous GDA, linear/logistic VRGDA, dynamic pricing

// Example: setting up and querying a GDA auction
 
use origami_defi::auction::gda::{DiscreteGDA, DiscreteGDATrait};
use cubit::f128::types::fixed::{Fixed, FixedTrait};
 
#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct ItemAuction {
    #[key] pub item_id: u32,
    pub gda: DiscreteGDA,
    pub start_time: u64,
}
 
fn setup_auction(ref world: WorldStorage, item_id: u32) {
    let gda = DiscreteGDA {
        sold: FixedTrait::new_unscaled(0, false), // 0 items sold
        initial_price: FixedTrait::new_unscaled(1000, false), // 1000 units available
        scale_factor: FixedTrait::new_unscaled(11, false) / FixedTrait::new_unscaled(10, false), // 1.1 - each item costs more
        decay_constant: FixedTrait::new_unscaled(1, false) / FixedTrait::new_unscaled(100, false), // 0.01 - price decays over time
    };
    world.write_model(@ItemAuction { item_id, gda, start_time: get_block_timestamp() });
}
 
fn get_current_price(world: WorldStorage, item_id: u32, quantity: u32) -> u128 {
    let auction: ItemAuction = world.read_model(item_id);
    let time_elapsed = get_block_timestamp() - auction.start_time;
 
    // Calculate current GDA price for the quantity
    let price = auction.gda.purchase_price(
        FixedTrait::new(time_elapsed.into(), false),
        FixedTrait::new_unscaled(quantity.into(), false)
    );
 
    price.mag // Convert to u128
}

Rating

Competitive ranking systems for multiplayer games using the industry-standard Elo rating system.

Key Features: Elo rating calculation, configurable K-factors, win/loss/draw support

// Example: resolving a match and updating player ratings
 
use origami_rating::elo::EloTrait;
 
#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct PlayerRating {
    #[key] pub player: ContractAddress,
    pub rating: u64,
    pub games_played: u32,
}
 
fn resolve_match(
    ref world: WorldStorage,
    player1: ContractAddress,
    player2: ContractAddress,
    outcome: u8 // 0 = p2 wins, 50 = draw, 100 = p1 wins
) {
    let mut rating1: PlayerRating = world.read_model(player1);
    let mut rating2: PlayerRating = world.read_model(player2);
 
    // Calculate Elo changes
    let k_factor = if rating1.games_played < 30 { 40 } else { 20 };
    let (change1, is_negative1) = EloTrait::rating_change(
        rating1.rating, rating2.rating, outcome.into(), k_factor
    );
 
    // Apply rating changes
    rating1.rating = if is_negative1 { rating1.rating - change1 } else { rating1.rating + change1 };
    rating2.rating = if is_negative1 { rating2.rating + change1 } else { rating2.rating - change1 };
 
    rating1.games_played += 1;
    rating2.games_played += 1;
 
    world.write_model(@rating1);
    world.write_model(@rating2);
}

Security

Cryptographic primitives for secure game mechanics including commitment schemes for trustless gameplay.

Key Features: Commit-reveal schemes, cryptographic verification, front-running prevention

// Example: committing and revealing an action with a salt
 
use origami_security::commitment::{Commitment, CommitmentTrait};
use core::poseidon::poseidon_hash_span;
 
#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct PlayerCommitment {
    #[key] pub game_id: u32,
    #[key] pub player: ContractAddress,
    pub commitment_hash: felt252,
    pub revealed: bool,
    pub action: u8,
}
 
fn commit_action(ref world: WorldStorage, game_id: u32, player: ContractAddress, commitment_hash: felt252) {
    // Player calculates hash(action + salt) off-chain and submits only the hash
    world.write_model(@PlayerCommitment {
        game_id, player, commitment_hash, revealed: false, action: 0
    });
}
 
fn reveal_action(ref world: WorldStorage, game_id: u32, player: ContractAddress, action: u8, salt: felt252) -> bool {
    let mut commitment: PlayerCommitment = world.read_model((game_id, player));
    assert(!commitment.revealed, 'Already revealed');
 
    // Verify commitment
    let mut reveal_data = array![];
    action.serialize(ref reveal_data);
    salt.serialize(ref reveal_data);
    let is_valid = poseidon_hash_span(reveal_data.span()) == commitment.commitment_hash;
 
    if is_valid {
        commitment.revealed = true;
        commitment.action = action;
        world.write_model(@commitment);
    }
 
    is_valid
}