Skip to content

Commit

Permalink
Gas optimizations and general code cleanup (#369)
Browse files Browse the repository at this point in the history
* Changes public interface `start` to `new_game` to be more description
* Inputs to `new_game` have been greatly simplified, now requiring only: {Client Address, Weapon, Name}
* Removes unused variables {Home Realm, Class} from AdventurerMeta and expands storage space for Name
* Reduces gas for Packing/Unpacking by taking advantage of U256DivRem added in V0.12.1
* Changes public interface `drop_items` to `drop`
* Changes public interface `upgrade_adventurer` to `upgrade`
* Removes Gold from Beast Seed hash to reduce manipulation
* Adds block timestamp to Adventurer Seed hash to increase randomness
  • Loading branch information
loothero authored Sep 27, 2023
1 parent 5295473 commit 0666023
Show file tree
Hide file tree
Showing 7 changed files with 345 additions and 343 deletions.
15 changes: 6 additions & 9 deletions contracts/adventurer/src/adventurer.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -956,18 +956,15 @@ impl ImplAdventurer of IAdventurer {
specials
}

// @notice get_beast_seed provides an entropy source that is fixed during battle
// it intentionally does not use global_entropy as that could change during battle and this
// entropy allows us to simulate a persistent battle without having to store beast
// details on-chain.
// @param self A reference to the Adventurer object which represents the adventurer.
// @param adventurer_entropy A number used for randomization.
// @notice provides a a beast seed that is fixed during battle. This function does not use
// game entropy as that could change during battle resulting in the beast changing
// @param self A reference to the Adventurer to get the beast seed for.
// @param adventurer_entropy A u128 used to randomize the beast seed
// @return Returns a number used for generated a random beast.
fn get_beast_seed(self: Adventurer, adventurer_entropy: u128) -> u128 {
if self.get_level() > 1 {
let mut hash_span = ArrayTrait::new();
hash_span.append(self.xp.into());
hash_span.append(self.gold.into());
hash_span.append(adventurer_entropy.into());
let poseidon = poseidon_hash_span(hash_span.span());
let (d, r) = rshift_split(poseidon.into(), 340282366920938463463374607431768211455);
Expand Down Expand Up @@ -1409,12 +1406,12 @@ impl ImplAdventurer of IAdventurer {
}

fn get_randomness(
self: Adventurer, adventurer_entropy: u128, global_entropy: u128
self: Adventurer, adventurer_entropy: u128, game_entropy: u128
) -> (u128, u128) {
let mut hash_span = ArrayTrait::<felt252>::new();
hash_span.append(self.xp.into());
hash_span.append(adventurer_entropy.into());
hash_span.append(global_entropy.into());
hash_span.append(game_entropy.into());

let poseidon = poseidon_hash_span(hash_span.span());
let (d, r) = rshift_split(poseidon.into(), U128_MAX.into());
Expand Down
24 changes: 5 additions & 19 deletions contracts/adventurer/src/adventurer_meta.cairo
Original file line number Diff line number Diff line change
@@ -1,36 +1,26 @@
use option::OptionTrait;
use traits::{TryInto, Into};

use pack::constants::pow;
use pack::pack::{Packing, rshift_split};

#[derive(Drop, Copy, Serde)]
struct AdventurerMetadata {
name: u128,
home_realm: u16,
class: u8,
entropy: u128,
}

impl PackingAdventurerMetadata of Packing<AdventurerMetadata> {
fn pack(self: AdventurerMetadata) -> felt252 {
(self.entropy.into()
+ self.home_realm.into() * pow::TWO_POW_128
+ self.class.into() * pow::TWO_POW_141
+ self.name.into() * pow::TWO_POW_145)
+ self.name.into() * pow::TWO_POW_128)
.try_into()
.expect('pack AdventurerMetadata')
}
fn unpack(packed: felt252) -> AdventurerMetadata {
let packed = packed.into();
let (packed, entropy) = rshift_split(packed, pow::TWO_POW_128);
let (packed, home_realm) = rshift_split(packed, pow::TWO_POW_13);
let (packed, class) = rshift_split(packed, pow::TWO_POW_4);
let (_, name) = rshift_split(packed, pow::TWO_POW_107);
let (_, name) = rshift_split(packed, pow::TWO_POW_128);
AdventurerMetadata {
name: name.try_into().expect('unpack AdvMetadata name'),
home_realm: home_realm.try_into().expect('unpack AdvMetadata home_realm'),
class: class.try_into().expect('unpack AdvMetadata class'),
entropy: entropy.try_into().expect('unpack AdvMetadata entropy')
}
}
Expand All @@ -43,19 +33,15 @@ impl PackingAdventurerMetadata of Packing<AdventurerMetadata> {

#[cfg(test)]
#[test]
#[available_gas(50000000)]
fn test_meta() {
#[available_gas(96380)]
fn test_pack_unpack_adventurer_meta() {
let meta = AdventurerMetadata {
name: 'abcdefghijklm',
home_realm: 8000,
class: 1,
name: 'abcdefghijklmno',
entropy: 340282366920938463463374607431768211455
};

let packed = meta.pack();
let unpacked: AdventurerMetadata = Packing::unpack(packed);
assert(meta.name == unpacked.name, 'name');
assert(meta.home_realm == unpacked.home_realm, 'home_realm');
assert(meta.class == unpacked.class, 'class');
assert(meta.entropy == unpacked.entropy, 'entropy');
}
24 changes: 14 additions & 10 deletions contracts/adventurer/src/adventurer_utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,13 @@ impl AdventurerUtils of IAdventurerUtils {
// @param block_number The block number used in generating the entropy
// @param adventurer_id The ID of the adventurer used in generating the entropy
// @return A u128 type entropy unique to the block number and adventurer ID
fn generate_adventurer_entropy(block_number: u64, adventurer_id: u256) -> u128 {
fn generate_adventurer_entropy(
adventurer_id: u256, block_number: u64, block_timestamp: u64
) -> u128 {
let mut hash_span = ArrayTrait::<felt252>::new();
hash_span.append(block_number.into());
hash_span.append(adventurer_id.try_into().unwrap());
hash_span.append(block_number.into());
hash_span.append(block_timestamp.into());
let poseidon: felt252 = poseidon_hash_span(hash_span.span()).into();
let (d, r) = rshift_split(poseidon.into(), U128_MAX.into());
r.try_into().unwrap()
Expand Down Expand Up @@ -181,15 +184,15 @@ impl AdventurerUtils of IAdventurerUtils {
// @notice gets randomness for adventurer
// @param adventurer_xp: adventurer xp
// @param adventurer_entropy: adventurer entropy
// @param global_entropy: global entropy
// @param game_entropy: game entropy
// @return (u128, u128): tuple of randomness
fn get_randomness(
adventurer_xp: u16, adventurer_entropy: u128, global_entropy: u128
adventurer_xp: u16, adventurer_entropy: u128, game_entropy: u128
) -> (u128, u128) {
let mut hash_span = ArrayTrait::<felt252>::new();
hash_span.append(adventurer_xp.into());
hash_span.append(adventurer_entropy.into());
hash_span.append(global_entropy.into());
hash_span.append(game_entropy.into());
let poseidon = poseidon_hash_span(hash_span.span());
AdventurerUtils::split_hash(poseidon)
}
Expand All @@ -198,16 +201,16 @@ impl AdventurerUtils of IAdventurerUtils {
// @param adventurer_xp: adventurer xp
// @param adventurer_entropy: adventurer entropy
// @param adventurer_health: adventurer health
// @param global_entropy: global entropy
// @param game_entropy: game entropy
// @return (u128, u128): tuple of randomness
fn get_randomness_with_health(
adventurer_xp: u16, adventurer_health: u16, adventurer_entropy: u128, global_entropy: u128
adventurer_xp: u16, adventurer_health: u16, adventurer_entropy: u128, game_entropy: u128
) -> (u128, u128) {
let mut hash_span = ArrayTrait::<felt252>::new();
hash_span.append(adventurer_xp.into());
hash_span.append(adventurer_health.into());
hash_span.append(adventurer_entropy.into());
hash_span.append(global_entropy.into());
hash_span.append(game_entropy.into());
let poseidon = poseidon_hash_span(hash_span.span());
AdventurerUtils::split_hash(poseidon)
}
Expand Down Expand Up @@ -305,7 +308,7 @@ mod tests {
adventurer_utils::AdventurerUtils
};
use combat::constants::CombatEnums::{Type, Tier, Slot};
#[test]
#[test]
#[available_gas(166544)]
fn test_generate_starting_stats_gas() {
AdventurerUtils::generate_starting_stats(0, 1);
Expand Down Expand Up @@ -535,8 +538,9 @@ mod tests {
}
let adventurer_id: u256 = i;
let block_number = 839152;
let block_timestamp = 53289712;
let adventurer_entropy = AdventurerUtils::generate_adventurer_entropy(
block_number, adventurer_id
adventurer_id, block_number, block_timestamp
);
i += 1;
};
Expand Down
32 changes: 13 additions & 19 deletions contracts/game/src/game/interfaces.cairo
Original file line number Diff line number Diff line change
@@ -1,37 +1,34 @@
use starknet::ContractAddress;
use market::market::{ItemPurchase};
use beasts::beast::Beast;
use survivor::{
bag::Bag, adventurer::{Adventurer, Stats}, adventurer_meta::AdventurerMetadata,
item_meta::{ItemSpecials, ItemSpecialsStorage}
};
use lootitems::loot::{Loot};
use market::market::{ItemPurchase};
use beasts::beast::Beast;


#[starknet::interface]
trait IGame<TContractState> {
// actions ---------------------------------------------------
fn start(
ref self: TContractState,
client_reward_address: ContractAddress,
starting_weapon: u8,
adventurer_meta: AdventurerMetadata
// ------ Game Actions ------
fn new_game(
ref self: TContractState, client_reward_address: ContractAddress, weapon: u8, name: u128
);
fn explore(ref self: TContractState, adventurer_id: u256, till_beast: bool);
fn attack(ref self: TContractState, adventurer_id: u256, to_the_death: bool);
fn flee(ref self: TContractState, adventurer_id: u256, to_the_death: bool);
fn equip(ref self: TContractState, adventurer_id: u256, items: Array<u8>);
fn drop_items(ref self: TContractState, adventurer_id: u256, items: Array<u8>);
fn upgrade_adventurer(
fn drop(ref self: TContractState, adventurer_id: u256, items: Array<u8>);
fn upgrade(
ref self: TContractState,
adventurer_id: u256,
potions: u8,
stat_upgrades: Stats,
items: Array<ItemPurchase>,
);
fn slay_idle_adventurers(ref self: TContractState, adventurer_ids: Array<u256>);
fn rotate_global_entropy(ref self: TContractState);
fn rotate_game_entropy(ref self: TContractState);

// --------- view functions ---------
// ------ View Functions ------

// adventurer details
fn get_adventurer(self: @TContractState, adventurer_id: u256) -> Adventurer;
Expand All @@ -54,7 +51,6 @@ trait IGame<TContractState> {
fn get_charisma(self: @TContractState, adventurer_id: u256) -> u8;

// item stats
// TODO: get_equipped_items(self: @TContractState, adventurer_id: u256) -> Array<u8>;
fn get_weapon_greatness(self: @TContractState, adventurer_id: u256) -> u8;
fn get_chest_greatness(self: @TContractState, adventurer_id: u256) -> u8;
fn get_head_greatness(self: @TContractState, adventurer_id: u256) -> u8;
Expand Down Expand Up @@ -106,14 +102,12 @@ trait IGame<TContractState> {
fn get_beast_type(self: @TContractState, beast_id: u8) -> u8;
fn get_beast_tier(self: @TContractState, beast_id: u8) -> u8;

// TODO: Game settings
fn next_global_entropy_rotation(self: @TContractState) -> felt252;
// game settings
fn next_game_entropy_rotation(self: @TContractState) -> felt252;

// contract details
fn owner_of(self: @TContractState, adventurer_id: u256) -> ContractAddress;
fn get_dao_address(self: @TContractState) -> ContractAddress;
fn get_lords_address(self: @TContractState) -> ContractAddress;
fn get_entropy(self: @TContractState) -> u64;

// checks ----------------------------------------------------
fn owner_of(self: @TContractState, adventurer_id: u256) -> ContractAddress;
}
Loading

1 comment on commit 0666023

@vercel
Copy link

@vercel vercel bot commented on 0666023 Sep 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.