Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add first leaderboard model implementation with tests #39

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

coxmars
Copy link
Contributor

@coxmars coxmars commented Sep 11, 2024

📝 Pull Request Overview

🔄 Changes Made

  • Leaderboard Model Creation:

    • Added a new Leaderboard model in leaderboard.cairo with the following attributes:
      • leaderboard_id
      • name
      • description
      • entries
      • last_updated
    • Implemented LeaderboardEntry struct to represent individual entries in the leaderboard.
  • Comprehensive Tests:

    • Added unit tests for the Leaderboard model including:
      • Testing addition of single and multiple entries
      • Verifying leaderboard capacity limits
      • Validating entry properties

🔧 Testing

image

🔜 Next Steps

  • I need to add more functions to establish score logic, create a function to order the list, use a dynamic structure etc.

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive leaderboard system for tracking player performance, including structures for Leaderboard and LeaderboardEntry.
    • Added functionalities to manage leaderboard entries, including adding new entries and checking leaderboard status.
  • Documentation

    • New TOML files created for bytebeasts-Leaderboard and bytebeasts-LeaderboardEntry models, outlining their structure and properties.
  • Tests

    • Implemented unit tests to validate leaderboard functionalities, ensuring accurate tracking and management of player rankings.

#[dojo::model]
pub struct LeaderboardEntry {
#[key]
pub player_address: ContractAddress, // On-chain player address
Copy link
Contributor

Choose a reason for hiding this comment

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

As we are handling the player just with an ID instead of the address, we can keep that behavior here. What do you think?

pub name: felt252, // Leaderboard name (e.g., "Global", "Monthly")
pub description: felt252, // Description of what this leaderboard tracks
pub entries: Array<LeaderboardEntry>, // List of leaderboard entries
pub last_updated: u64, // Timestamp of last update
Copy link
Contributor

Choose a reason for hiding this comment

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

what is the idea to use u64 here for the timestamp? are we going to use a format like this? 20240708

// Add new entry
self.entries.append(entry);
// Update timestamp
self.last_updated = starknet::get_block_timestamp();
Copy link
Contributor

Choose a reason for hiding this comment

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

what is the format of the returned value of this function? 😮

use super::{LeaderboardTrait, MAX_ENTRIES};
use bytebeasts::models::leaderboard::{Leaderboard, LeaderboardEntry};

fn create_mock_entry(address: ContractAddress, name: felt252, score: u32, wins: u32, losses: u32, highest_score: u32, is_active: bool) -> LeaderboardEntry {
Copy link
Contributor

Choose a reason for hiding this comment

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

🚀

let mut leaderboard = create_empty_leaderboard();
let entry = create_mock_entry(starknet::get_contract_address(), 'Alice', 100, 20, 5, 1000, true);

leaderboard.add_entry(entry).expect('Failed to add entry');
Copy link
Contributor

Choose a reason for hiding this comment

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

what .expect do?

Copy link

@juandiegocv27 juandiegocv27 left a comment

Choose a reason for hiding this comment

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

love the idea for the game and i would be a marvelous implementation, but please take a look of @danielcdz review first and fix those errors

Copy link

coderabbitai bot commented Oct 28, 2024

Walkthrough

The pull request introduces a comprehensive implementation of a leaderboard model within the bytebeasts context. It includes two main JSON files that define the data structures and interfaces necessary for managing leaderboard entries and overall leaderboard data. Key structures such as LeaderboardEntry and Leaderboard are defined, capturing player-specific statistics and leaderboard metadata, respectively. The implementation of the IModel interface through DojoModelImpl facilitates interaction with these data structures.

Additionally, several enumerations and span structures are introduced to handle optional values and collections of data effectively. The TOML files provide the necessary configuration for the models, detailing their members and types. A new module for the leaderboard is added to the existing library structure, along with a dedicated file that encapsulates the logic for managing leaderboard behavior, including methods for adding entries and checking leaderboard capacity. This implementation is supported by unit tests to ensure functionality.

Assessment against linked issues

Objective Addressed Explanation
Implement a leaderboard model that tracks and ranks players based on their achievements, battle wins, and other key performance metrics. (#28)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Outside diff range and nitpick comments (4)
manifests/dev/base/abis/models/bytebeasts-LeaderboardEntry-7237950c.json (2)

364-368: Consider adding implementation documentation

While the implementation is correct, consider adding a documentation comment describing the purpose and responsibilities of the leaderboard_entryImpl.


421-438: Consider expanding interface with required functionality

Based on the PR objectives, the interface should include methods for:

  1. Score calculation and updates
  2. Rank management
  3. Entry ordering
  4. Entry validation

This will help implement the mentioned requirements for score logic and leaderboard ordering.

Example additions:

 {
   "type": "interface",
   "name": "bytebeasts::models::leaderboard::Ileaderboard_entry",
   "items": [
     {
       "type": "function",
       "name": "ensure_abi",
       "inputs": [
         {
           "name": "model",
           "type": "bytebeasts::models::leaderboard::LeaderboardEntry"
         }
       ],
       "outputs": [],
       "state_mutability": "view"
     },
+    {
+      "type": "function",
+      "name": "update_score",
+      "inputs": [
+        {
+          "name": "new_score",
+          "type": "core::integer::u32"
+        }
+      ],
+      "outputs": [],
+      "state_mutability": "external"
+    },
+    {
+      "type": "function",
+      "name": "update_rank",
+      "inputs": [
+        {
+          "name": "new_rank",
+          "type": "core::integer::u32"
+        }
+      ],
+      "outputs": [],
+      "state_mutability": "external"
+    }
   ]
 }
manifests/dev/base/abis/models/bytebeasts-Leaderboard-7e680376.json (2)

384-420: Consider adding validation constraints for LeaderboardEntry fields.

While the structure captures all necessary player statistics, consider adding validation constraints for:

  • Score and rank ranges
  • Player name length/format
  • Win/loss ratio validation

This will help maintain data integrity and prevent invalid entries.


422-446: Consider adding capacity constraints for the entries array.

The Leaderboard structure should define a maximum capacity for entries to:

  • Prevent unbounded growth
  • Ensure consistent performance
  • Manage memory usage effectively

Consider adding a max_capacity field or implementing a fixed-size array.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between acc9b14 and 058ddb1.

📒 Files selected for processing (6)
  • manifests/dev/base/abis/models/bytebeasts-Leaderboard-7e680376.json (1 hunks)
  • manifests/dev/base/abis/models/bytebeasts-LeaderboardEntry-7237950c.json (1 hunks)
  • manifests/dev/base/models/bytebeasts-Leaderboard-7e680376.toml (1 hunks)
  • manifests/dev/base/models/bytebeasts-LeaderboardEntry-7237950c.toml (1 hunks)
  • src/lib.cairo (1 hunks)
  • src/models/leaderboard.cairo (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • manifests/dev/base/models/bytebeasts-Leaderboard-7e680376.toml
🔇 Additional comments (7)
src/lib.cairo (1)

25-25: LGTM! Module declaration follows project structure.

The addition of the leaderboard module in the models section is well-placed and follows the existing architectural patterns.

Let's verify the implementation details of the leaderboard module:

✅ Verification successful

LGTM! Leaderboard module is well-implemented with comprehensive functionality.

The leaderboard module implementation is robust and complete:

  • Properly structured with LeaderboardEntry and Leaderboard models using #[dojo::model]
  • Implements core functionality with LeaderboardBehavior trait
  • Includes comprehensive test coverage with multiple test cases
  • Has proper validation (e.g., max entries limit)
  • Well-documented with clear comments
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the leaderboard module implementation matches the JSON contract

# Test 1: Check if the leaderboard module exists and contains expected structures
ast-grep --pattern 'struct Leaderboard {
  $$$
}'

# Test 2: Check if the module implements the IModel interface
ast-grep --pattern 'impl $_ of IModel {
  $$$
}'

# Test 3: Look for test coverage
fd "test.*leaderboard.*cairo$"

Length of output: 135


Script:

#!/bin/bash
# Let's try a different approach to verify the leaderboard implementation

# Check if the leaderboard module file exists and show its content
fd "leaderboard.cairo$" --exec cat {}

# Search for any references to leaderboard in the codebase
rg -i "leaderboard" -A 3 -B 3

# Look for any model-related interfaces or traits that might be implemented
rg "trait IModel" -A 5

Length of output: 25670

manifests/dev/base/models/bytebeasts-LeaderboardEntry-7237950c.toml (3)

18-27: Consider adding constraints for score and rank.

A few suggestions for the score and rank fields:

  1. Add validation to ensure score is non-negative
  2. Consider if rank should be a key field to enforce uniqueness
  3. Add documentation about how rank updates are handled (is it automatically calculated based on score?)

Let's check for similar ranking implementations in the codebase:

#!/bin/bash
# Search for similar ranking implementations
rg -A 5 "name.*rank" --type toml

13-17: Consider using a string type for player_name.

The felt252 type might have limitations for storing player names. Consider if a string type would be more appropriate to handle various character sets and name lengths.

Let's check if there are any existing string implementations in the codebase:

#!/bin/bash
# Search for string type implementations
rg -l "type.*String" --type toml

1-7: LGTM! Verify ABI file existence.

The model metadata is well-structured with appropriate class hashes and consistent naming.

Let's verify the referenced ABI file exists:

✅ Verification successful

ABI file verification successful

The referenced ABI file exists at the correct location and follows the expected naming convention. The model metadata is properly configured with the corresponding ABI file.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the ABI file exists and is valid JSON
if [ -f "manifests/dev/base/abis/models/bytebeasts-LeaderboardEntry-7237950c.json" ]; then
    jq . "manifests/dev/base/abis/models/bytebeasts-LeaderboardEntry-7237950c.json" > /dev/null
    echo "ABI file exists and is valid JSON"
else
    echo "ABI file not found"
fi

Length of output: 205


Script:

#!/bin/bash
# Let's try a different approach to verify the ABI file
ls -l manifests/dev/base/abis/models/bytebeasts-LeaderboardEntry-7237950c.json 2>/dev/null || echo "File not found"

# Also search for any similar ABI files to ensure we're looking in the right place
fd LeaderboardEntry.*.json manifests/

Length of output: 317

manifests/dev/base/abis/models/bytebeasts-LeaderboardEntry-7237950c.json (1)

1-363: LGTM: Core model implementation follows Dojo standards

The implementation properly implements the IModel interface with all required methods and includes comprehensive layout and introspection capabilities.

manifests/dev/base/abis/models/bytebeasts-Leaderboard-7e680376.json (2)

1-363: LGTM: Core model implementation follows Dojo standards.

The implementation correctly follows the Dojo model patterns with all required interface methods properly defined.


384-446: Verify alignment with battle tracking requirements.

While the current implementation tracks basic metrics, consider adding battle-specific fields to better align with issue #28 requirements:

  • Battle participation count
  • Battle win streak
  • Last battle timestamp
  • Battle-specific achievements
✅ Verification successful

Leaderboard model aligns well with battle system implementation

The current leaderboard model implementation with wins, losses, and scores is sufficient and well-aligned with the battle system's functionality. The battle system tracks battle outcomes through the battle_active state and emits appropriate events for wins/losses, which can directly update the leaderboard metrics. Additional suggested fields are unnecessary because:

  • Battle participation is implicitly tracked through wins + losses
  • Win streaks can be computed from the battle history if needed
  • Battle timestamps are handled by last_updated field
  • Battle-specific achievements would be better suited in a separate achievements system
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for battle-related functionality in the codebase
echo "Searching for battle-related files and implementations..."
fd -e cairo battle
rg "battle|combat|fight" -t cairo

Length of output: 288


Script:

#!/bin/bash
# Let's examine the battle-related files content
echo "=== Checking battle model structure ==="
cat src/models/battle.cairo

echo -e "\n=== Checking battle system implementation ==="
cat src/systems/battle.cairo

Length of output: 8041

Comment on lines +8 to +46
[[members]]
name = "player_address"
type = "ContractAddress"
key = true

[[members]]
name = "player_name"
type = "felt252"
key = false

[[members]]
name = "score"
type = "u32"
key = false

[[members]]
name = "rank"
type = "u32"
key = false

[[members]]
name = "wins"
type = "u32"
key = false

[[members]]
name = "losses"
type = "u32"
key = false

[[members]]
name = "highest_score"
type = "u32"
key = false

[[members]]
name = "is_active"
type = "bool"
key = false
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding timestamp fields.

To improve data tracking and enable features like:

  • Leaderboard history
  • Activity monitoring
  • Seasonal rankings

Consider adding:

  • created_at: When the entry was first created
  • updated_at: Last time any stats were modified

Also, for future scalability, consider:

  1. Adding a season_id field if you plan to implement seasonal leaderboards
  2. Including a total_games field (wins + losses might not cover draws/disconnects)

Comment on lines +383 to +420
{
"type": "struct",
"name": "bytebeasts::models::leaderboard::LeaderboardEntry",
"members": [
{
"name": "player_address",
"type": "core::starknet::contract_address::ContractAddress"
},
{
"name": "player_name",
"type": "core::felt252"
},
{
"name": "score",
"type": "core::integer::u32"
},
{
"name": "rank",
"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"
}
]
},
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding timestamp and validation constraints

The LeaderboardEntry struct has good coverage of player statistics, but consider these enhancements:

  1. Add timestamps for entry creation and last update to track entry history
  2. Consider adding validation constraints for:
    • Maximum player name length
    • Score ranges
    • Rank boundaries

This will help maintain data integrity and provide better tracking capabilities.

Example addition:

 {
   "type": "struct",
   "name": "bytebeasts::models::leaderboard::LeaderboardEntry",
   "members": [
     {
       "name": "player_address",
       "type": "core::starknet::contract_address::ContractAddress"
     },
+    {
+      "name": "created_at",
+      "type": "core::integer::u64"
+    },
+    {
+      "name": "updated_at",
+      "type": "core::integer::u64"
+    },
     // ... existing fields ...
   ]
 }

Committable suggestion was skipped due to low confidence.

Comment on lines +439 to +445
{
"type": "event",
"name": "bytebeasts::models::leaderboard::leaderboard_entry::Event",
"kind": "enum",
"variants": []
}
]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add essential events for state changes

The Event enum is currently empty. Consider adding events for important state changes:

  1. Entry creation
  2. Score updates
  3. Rank changes
  4. Status changes (active/inactive)

These events will help track changes and enable external systems to react to leaderboard updates.

Example implementation:

 {
   "type": "event",
   "name": "bytebeasts::models::leaderboard::leaderboard_entry::Event",
   "kind": "enum",
-  "variants": []
+  "variants": [
+    {
+      "name": "EntryCreated",
+      "type": "bytebeasts::models::leaderboard::LeaderboardEntry"
+    },
+    {
+      "name": "ScoreUpdated",
+      "type": "(core::starknet::contract_address::ContractAddress, core::integer::u32)"
+    },
+    {
+      "name": "RankChanged",
+      "type": "(core::starknet::contract_address::ContractAddress, core::integer::u32)"
+    },
+    {
+      "name": "StatusChanged",
+      "type": "(core::starknet::contract_address::ContractAddress, core::bool)"
+    }
+  ]
 }

Committable suggestion was skipped due to low confidence.

Comment on lines +466 to +470
"type": "event",
"name": "bytebeasts::models::leaderboard::leaderboard::Event",
"kind": "enum",
"variants": []
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add essential leaderboard events.

The empty Event type should include crucial events such as:

  • Entry added/updated/removed
  • Rank changes
  • Score updates
  • Leaderboard reset

These events are essential for:

  • Tracking leaderboard changes
  • Implementing notifications
  • Auditing purposes

Example event structure:

{
  "type": "event",
  "name": "bytebeasts::models::leaderboard::leaderboard::Event",
  "kind": "enum",
  "variants": [
    {
      "name": "EntryAdded",
      "type": "bytebeasts::models::leaderboard::LeaderboardEntry"
    },
    {
      "name": "EntryUpdated",
      "type": "(bytebeasts::models::leaderboard::LeaderboardEntry, bytebeasts::models::leaderboard::LeaderboardEntry)"
    },
    {
      "name": "EntryRemoved",
      "type": "core::starknet::contract_address::ContractAddress"
    },
    {
      "name": "LeaderboardReset",
      "type": "core::integer::u64"
    }
  ]
}

Comment on lines +36 to +39
trait LeaderboardBehavior {
fn add_entry(ref self: Leaderboard, entry: LeaderboardEntry) -> Result<(), felt252>;
fn is_full(self: @Leaderboard) -> bool;
}

This comment was marked as spam.

Comment on lines +43 to +50
fn add_entry(ref self: Leaderboard, mut entry: LeaderboardEntry) -> Result<(), felt252> {
if self.is_full() {
return Result::Err('Leaderboard is full');
}
// Assign an initial value
entry.rank = self.entries.len() + 1;
// Add new entry
self.entries.append(entry);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent duplicate entries for the same player

There is no check to prevent adding multiple entries for the same player. This could lead to duplicate entries on the leaderboard. Consider checking if the player's address already exists in the leaderboard before adding a new entry.

You can add a check like this:

// Before adding the new entry
+ // Check for existing entry
+ if self.entries.iter().any(|e| e.player_address == entry.player_address) {
+     return Result::Err('Player already exists in leaderboard');
+ }

// Assign an initial rank or update ranks after sorting
entry.rank = self.entries.len() + 1;

Committable suggestion was skipped due to low confidence.

Comment on lines +48 to +54
entry.rank = self.entries.len() + 1;
// Add new entry
self.entries.append(entry);
// Update timestamp
self.last_updated = starknet::get_block_timestamp();
Result::Ok(())
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Rank assignment should be based on player score, not insertion order

Currently, entry.rank is assigned based on the order of insertion (self.entries.len() + 1), which may not accurately reflect the players' standings. To properly rank players, consider sorting the entries by score after adding a new entry and updating the ranks accordingly.

Here's how you might adjust the code:

// Add new entry
self.entries.append(entry);

+ // Sort entries by score in descending order
+ self.entries.sort_unstable_by(|a, b| b.score.cmp(&a.score));

+ // Update ranks
+ let mut rank = 1_u32;
+ for e in self.entries.iter_mut() {
+     e.rank = rank;
+     rank += 1;
+ }

// Update timestamp
self.last_updated = starknet::get_block_timestamp();

Committable suggestion was skipped due to low confidence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement leaderboard model/entity and functions
4 participants