From b90aeea6bd17075e891ad49fecfb86e8093eb69b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Membre=C3=B1o=20Brenes?= <130603817+KevinMB0220@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:47:48 -0600 Subject: [PATCH] feat-System leaderboard (#104) * feat-System leaderboard * update feat-System leaderboard --- ...ytebeasts-leaderboard_system-63f2c524.json | 477 ++++++++++++++++++ ...ytebeasts-leaderboard_system-63f2c524.toml | 10 + src/lib.cairo | 1 + src/systems/leaderboard.cairo | 178 +++++++ 4 files changed, 666 insertions(+) create mode 100644 manifests/dev/base/abis/contracts/bytebeasts-leaderboard_system-63f2c524.json create mode 100644 manifests/dev/base/contracts/bytebeasts-leaderboard_system-63f2c524.toml create mode 100644 src/systems/leaderboard.cairo diff --git a/manifests/dev/base/abis/contracts/bytebeasts-leaderboard_system-63f2c524.json b/manifests/dev/base/abis/contracts/bytebeasts-leaderboard_system-63f2c524.json new file mode 100644 index 0000000..1dd480b --- /dev/null +++ b/manifests/dev/base/abis/contracts/bytebeasts-leaderboard_system-63f2c524.json @@ -0,0 +1,477 @@ +[ + { + "type": "impl", + "name": "ContractImpl", + "interface_name": "dojo::contract::contract::IContract" + }, + { + "type": "struct", + "name": "core::byte_array::ByteArray", + "members": [ + { + "name": "data", + "type": "core::array::Array::" + }, + { + "name": "pending_word", + "type": "core::felt252" + }, + { + "name": "pending_word_len", + "type": "core::integer::u32" + } + ] + }, + { + "type": "interface", + "name": "dojo::contract::contract::IContract", + "items": [ + { + "type": "function", + "name": "contract_name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "name_hash", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_hash", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "WorldProviderImpl", + "interface_name": "dojo::world::world_contract::IWorldProvider" + }, + { + "type": "struct", + "name": "dojo::world::world_contract::IWorldDispatcher", + "members": [ + { + "name": "contract_address", + "type": "core::starknet::contract_address::ContractAddress" + } + ] + }, + { + "type": "interface", + "name": "dojo::world::world_contract::IWorldProvider", + "items": [ + { + "type": "function", + "name": "world", + "inputs": [], + "outputs": [ + { + "type": "dojo::world::world_contract::IWorldDispatcher" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "LeaderboardActionImpl", + "interface_name": "bytebeasts::systems::leaderboard::ILeaderboardAction" + }, + { + "type": "enum", + "name": "core::bool", + "variants": [ + { + "name": "False", + "type": "()" + }, + { + "name": "True", + "type": "()" + } + ] + }, + { + "type": "struct", + "name": "bytebeasts::models::leaderboard::LeaderboardEntry", + "members": [ + { + "name": "player_id", + "type": "core::integer::u32" + }, + { + "name": "player_name", + "type": "core::felt252" + }, + { + "name": "score", + "type": "core::integer::u32" + }, + { + "name": "wins", + "type": "core::integer::u32" + }, + { + "name": "losses", + "type": "core::integer::u32" + }, + { + "name": "highest_score", + "type": "core::integer::u32" + }, + { + "name": "is_active", + "type": "core::bool" + } + ] + }, + { + "type": "interface", + "name": "bytebeasts::systems::leaderboard::ILeaderboardAction", + "items": [ + { + "type": "function", + "name": "create_leaderboard", + "inputs": [ + { + "name": "leaderboard_id", + "type": "core::integer::u64" + }, + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "description", + "type": "core::felt252" + }, + { + "name": "last_updated", + "type": "core::integer::u64" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "add_entry", + "inputs": [ + { + "name": "leaderboard_id", + "type": "core::integer::u64" + }, + { + "name": "player_id", + "type": "core::integer::u32" + }, + { + "name": "player_name", + "type": "core::felt252" + }, + { + "name": "score", + "type": "core::integer::u32" + }, + { + "name": "wins", + "type": "core::integer::u32" + }, + { + "name": "losses", + "type": "core::integer::u32" + }, + { + "name": "highest_score", + "type": "core::integer::u32" + }, + { + "name": "is_active", + "type": "core::bool" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "get_all_entries", + "inputs": [ + { + "name": "leaderboard_id", + "type": "core::integer::u64" + } + ], + "outputs": [ + { + "type": "core::array::Array::" + } + ], + "state_mutability": "external" + }, + { + "type": "function", + "name": "remove_entry", + "inputs": [ + { + "name": "leaderboard_id", + "type": "core::integer::u64" + }, + { + "name": "player_id", + "type": "core::integer::u32" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "update_entry", + "inputs": [ + { + "name": "leaderboard_id", + "type": "core::integer::u64" + }, + { + "name": "player_id", + "type": "core::integer::u32" + }, + { + "name": "player_name", + "type": "core::felt252" + }, + { + "name": "score", + "type": "core::integer::u32" + }, + { + "name": "wins", + "type": "core::integer::u32" + }, + { + "name": "losses", + "type": "core::integer::u32" + }, + { + "name": "highest_score", + "type": "core::integer::u32" + }, + { + "name": "is_active", + "type": "core::bool" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "calculate_score", + "inputs": [ + { + "name": "wins", + "type": "core::integer::u32" + }, + { + "name": "highest_score", + "type": "core::integer::u32" + }, + { + "name": "losses", + "type": "core::integer::u32" + } + ], + "outputs": [ + { + "type": "core::integer::u32" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "get_slice", + "inputs": [ + { + "name": "leaderboard_id", + "type": "core::integer::u64" + }, + { + "name": "start", + "type": "core::integer::u32" + }, + { + "name": "end", + "type": "core::integer::u32" + } + ], + "outputs": [ + { + "type": "core::array::Array::" + } + ], + "state_mutability": "external" + }, + { + "type": "function", + "name": "upgrade_entry_stats", + "inputs": [ + { + "name": "leaderboard_id", + "type": "core::integer::u64" + }, + { + "name": "player_id", + "type": "core::integer::u32" + }, + { + "name": "new_wins", + "type": "core::integer::u32" + }, + { + "name": "new_losses", + "type": "core::integer::u32" + }, + { + "name": "new_highest_score", + "type": "core::integer::u32" + } + ], + "outputs": [], + "state_mutability": "external" + } + ] + }, + { + "type": "impl", + "name": "IDojoInitImpl", + "interface_name": "bytebeasts::systems::leaderboard::leaderboard_system::IDojoInit" + }, + { + "type": "interface", + "name": "bytebeasts::systems::leaderboard::leaderboard_system::IDojoInit", + "items": [ + { + "type": "function", + "name": "dojo_init", + "inputs": [], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "UpgradableImpl", + "interface_name": "dojo::contract::upgradeable::IUpgradeable" + }, + { + "type": "interface", + "name": "dojo::contract::upgradeable::IUpgradeable", + "items": [ + { + "type": "function", + "name": "upgrade", + "inputs": [ + { + "name": "new_class_hash", + "type": "core::starknet::class_hash::ClassHash" + } + ], + "outputs": [], + "state_mutability": "external" + } + ] + }, + { + "type": "event", + "name": "dojo::contract::upgradeable::upgradeable::Upgraded", + "kind": "struct", + "members": [ + { + "name": "class_hash", + "type": "core::starknet::class_hash::ClassHash", + "kind": "data" + } + ] + }, + { + "type": "event", + "name": "dojo::contract::upgradeable::upgradeable::Event", + "kind": "enum", + "variants": [ + { + "name": "Upgraded", + "type": "dojo::contract::upgradeable::upgradeable::Upgraded", + "kind": "nested" + } + ] + }, + { + "type": "event", + "name": "bytebeasts::systems::leaderboard::leaderboard_system::Event", + "kind": "enum", + "variants": [ + { + "name": "UpgradeableEvent", + "type": "dojo::contract::upgradeable::upgradeable::Event", + "kind": "nested" + } + ] + } +] \ No newline at end of file diff --git a/manifests/dev/base/contracts/bytebeasts-leaderboard_system-63f2c524.toml b/manifests/dev/base/contracts/bytebeasts-leaderboard_system-63f2c524.toml new file mode 100644 index 0000000..95157bc --- /dev/null +++ b/manifests/dev/base/contracts/bytebeasts-leaderboard_system-63f2c524.toml @@ -0,0 +1,10 @@ +kind = "DojoContract" +class_hash = "0x1a7893ed15f19f9e8233d342a0a0bea37c58fdcb54b10d867fb2269c77de125" +original_class_hash = "0x1a7893ed15f19f9e8233d342a0a0bea37c58fdcb54b10d867fb2269c77de125" +base_class_hash = "0x0" +abi = "manifests/dev/base/abis/contracts/bytebeasts-leaderboard_system-63f2c524.json" +reads = [] +writes = [] +init_calldata = [] +tag = "bytebeasts-leaderboard_system" +manifest_name = "bytebeasts-leaderboard_system-63f2c524" diff --git a/src/lib.cairo b/src/lib.cairo index aa41f96..f97fce0 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -6,6 +6,7 @@ mod systems { mod world_setup; mod bag; mod tournament; + mod leaderboard; } mod models { diff --git a/src/systems/leaderboard.cairo b/src/systems/leaderboard.cairo new file mode 100644 index 0000000..9078e73 --- /dev/null +++ b/src/systems/leaderboard.cairo @@ -0,0 +1,178 @@ +use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; +use bytebeasts::models::leaderboard::{Leaderboard, LeaderboardEntry, LeaderboardTrait}; +use alexandria_data_structures::array_ext::ArrayTraitExt; + +#[dojo::interface] +trait ILeaderboardAction { + fn create_leaderboard( + ref world: IWorldDispatcher, + leaderboard_id: u64, + name: felt252, + description: felt252, + last_updated: u64, + ); + + fn add_entry( + ref world: IWorldDispatcher, + leaderboard_id: u64, + player_id: u32, + player_name: felt252, + score: u32, + wins: u32, + losses: u32, + highest_score: u32, + is_active: bool, + ); + + fn get_all_entries(ref world: IWorldDispatcher, leaderboard_id: u64) -> Array; + + fn remove_entry(ref world: IWorldDispatcher, leaderboard_id: u64, player_id: u32); + + fn update_entry( + ref world: IWorldDispatcher, + leaderboard_id: u64, + player_id: u32, + player_name: felt252, + score: u32, + wins: u32, + losses: u32, + highest_score: u32, + is_active: bool, + ); + + fn calculate_score(wins: u32, highest_score: u32, losses: u32) -> u32; + + fn get_slice( + ref world: IWorldDispatcher, + leaderboard_id: u64, + start: u32, + end: u32, + ) -> Array; + + fn upgrade_entry_stats( + ref world: IWorldDispatcher, + leaderboard_id: u64, + player_id: u32, + new_wins: u32, + new_losses: u32, + new_highest_score: u32, + ); +} + +#[dojo::contract] +mod leaderboard_system { + use super::ILeaderboardAction; + use bytebeasts::models::leaderboard::{Leaderboard, LeaderboardEntry, LeaderboardTrait}; + + #[abi(embed_v0)] + impl LeaderboardActionImpl of ILeaderboardAction { + fn create_leaderboard( + ref world: IWorldDispatcher, + leaderboard_id: u64, + name: felt252, + description: felt252, + last_updated: u64, + ) { + let leaderboard = Leaderboard { + leaderboard_id, + name, + description, + entries: array![], + last_updated, + }; + set!(world, (leaderboard)); + } + + fn add_entry( + ref world: IWorldDispatcher, + leaderboard_id: u64, + player_id: u32, + player_name: felt252, + score: u32, + wins: u32, + losses: u32, + highest_score: u32, + is_active: bool, + ) { + let mut leaderboard = get!(world, leaderboard_id, Leaderboard); + let entry = LeaderboardEntry { + player_id, + player_name, + score, + wins, + losses, + highest_score, + is_active, + }; + leaderboard.add_entry(entry).unwrap(); + set!(world, (leaderboard)); + } + + fn get_all_entries(ref world: IWorldDispatcher, leaderboard_id: u64) -> Array { + let mut leaderboard = get!(world, leaderboard_id, Leaderboard); + leaderboard.get_entries() + } + + fn remove_entry(ref world: IWorldDispatcher, leaderboard_id: u64, player_id: u32) { + let mut leaderboard = get!(world, leaderboard_id, Leaderboard); + let index = leaderboard.get_index_by_player_id(player_id).unwrap(); + let entry = leaderboard.entries.at(index); + leaderboard.remove_entry(*entry).unwrap(); + set!(world, (leaderboard)); + } + + fn update_entry( + ref world: IWorldDispatcher, + leaderboard_id: u64, + player_id: u32, + player_name: felt252, + score: u32, + wins: u32, + losses: u32, + highest_score: u32, + is_active: bool, + ) { + let mut leaderboard = get!(world, leaderboard_id, Leaderboard); + let entry = LeaderboardEntry { + player_id, + player_name, + score, + wins, + losses, + highest_score, + is_active, + }; + leaderboard.update_entry(entry).unwrap(); + set!(world, (leaderboard)); + } + + fn calculate_score(wins: u32, highest_score: u32, losses: u32) -> u32 { + wins * 100 + highest_score - losses * 70 + } + + fn get_slice( + ref world: IWorldDispatcher, + leaderboard_id: u64, + start: u32, + end: u32, + ) -> Array { + let mut leaderboard = get!(world, leaderboard_id, Leaderboard); + leaderboard.get_slice(start, end).unwrap() + } + + fn upgrade_entry_stats( + ref world: IWorldDispatcher, + leaderboard_id: u64, + player_id: u32, + new_wins: u32, + new_losses: u32, + new_highest_score: u32, + ) { + let mut leaderboard = get!(world, leaderboard_id, Leaderboard); + leaderboard + .upgrade_entry_stats(player_id, new_wins, new_losses, new_highest_score) + .unwrap(); + set!(world, (leaderboard)); + } + } +}