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.

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
}