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: Implement player peformance tracking #240

Merged
merged 8 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion onchain/src/contracts/lyricsflip.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ pub mod LyricsFlip {
use core::poseidon::PoseidonTrait;
use lyricsflip::interfaces::lyricsflip::{ILyricsFlip};
use lyricsflip::utils::errors::Errors;
use lyricsflip::utils::types::{Answer, Card, Entropy, Genre, Round};

use lyricsflip::utils::types::{Card, Entropy, Genre, Round, Answer, PlayerStats};
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin_access::accesscontrol::{AccessControlComponent};
use openzeppelin_access::ownable::OwnableComponent;
Expand Down Expand Up @@ -47,6 +48,7 @@ pub mod LyricsFlip {
>, // round_id -> player_index -> player_address
round_players_count: Map<u64, u256>,
round_cards: Map<u64, Vec<u64>>, // round_id -> vec<card_ids>
player_stats: Map<ContractAddress, PlayerStats>, // player_address -> PlayerStats
round_ready_players: Map<
u64, Map<ContractAddress, bool>
>, // round_id -> player_address -> is_ready
Expand Down Expand Up @@ -218,6 +220,33 @@ pub mod LyricsFlip {
let is_ready = self.round_ready_players.entry(round_id).entry(caller_address).read();
assert(!is_ready, Errors::ALREADY_READY);

let round_players_count = self.round_players_count.entry(round_id).read();
let mut round_players = array![];
let mut idx = 0;
while (idx < round_players_count) {
round_players.append(self.round_players.entry(round_id).entry(idx).read());
idx += 1;
};

let mut index = 0_u32;
let mut i = 0_u256;
while (i < round_players_count) {
let curr_player: ContractAddress = *round_players.at(index);
let curr_player_stat = self.player_stats.entry(curr_player).read();
let new_total_rounds = curr_player_stat.total_rounds + 1;
let new_stats = PlayerStats {
total_rounds: new_total_rounds,
rounds_won: curr_player_stat.rounds_won,
current_streak: curr_player_stat.current_streak,
max_streak: curr_player_stat.max_streak
};

self.player_stats.entry(curr_player).write(new_stats);
index += 1;
i += 1;
};

//TODO: call the next_card function to get the first QuestionCard
// mark player as ready
self.round_ready_players.entry(round_id).entry(caller_address).write(true);

Expand Down Expand Up @@ -373,6 +402,10 @@ pub mod LyricsFlip {
cards.span()
}

fn get_player_stat(self: @ContractState, player: ContractAddress) -> PlayerStats {
self.player_stats.entry(player).read()
}

// TODO
fn submit_answer(self: @ContractState, answer: Answer) -> bool {
false
Expand Down
5 changes: 4 additions & 1 deletion onchain/src/interfaces/lyricsflip.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use lyricsflip::utils::types::{Answer, Card, Genre, Round};
use lyricsflip::utils::types::{Card, Genre, Round, Answer, PlayerStats};

use starknet::ContractAddress;

#[starknet::interface]
Expand All @@ -25,6 +26,8 @@ pub trait ILyricsFlip<TContractState> {
ref self: TContractState, recipient: ContractAddress, role: felt252, is_enable: bool,
);

fn get_player_stat(self: @TContractState, player: ContractAddress) -> PlayerStats;

//TODO
fn submit_answer(self: @TContractState, answer: Answer) -> bool;
}
77 changes: 77 additions & 0 deletions onchain/src/tests/test_lyricsflip.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ fn PLAYER_2() -> ContractAddress {
'PLAYER_2'.try_into().unwrap()
}

fn PLAYER_3() -> ContractAddress {
'PLAYER_3'.try_into().unwrap()
}

const ADMIN_ROLE: felt252 = selector!("ADMIN_ROLE");
const INVALID_ROLE: felt252 = selector!("INVALID_ROLE");

Expand Down Expand Up @@ -1007,3 +1011,76 @@ fn test_next_card_should_panic_with_completed_round() {
lyricsflip.next_card(round_id);
stop_cheat_caller_address(lyricsflip.contract_address);
}


#[test]
fn test_start_round_updates_player_stats() {
let lyricsflip = deploy();

start_cheat_caller_address(lyricsflip.contract_address, OWNER());
lyricsflip.set_role(ADMIN_ADDRESS(), ADMIN_ROLE, true);
stop_cheat_caller_address(lyricsflip.contract_address);

start_cheat_caller_address(lyricsflip.contract_address, ADMIN_ADDRESS());
for i in 0
..10_u64 {
let card = Card {
card_id: i.into(),
genre: Genre::HipHop,
artist: 'Bob Marley',
title: "",
year: 2000,
lyrics: "Lorem Ipsum",
};
lyricsflip.add_card(card);
};

let valid_cards_per_round = 5;
lyricsflip.set_cards_per_round(valid_cards_per_round);
stop_cheat_caller_address(lyricsflip.contract_address);

let seed = 1;

start_cheat_caller_address(lyricsflip.contract_address, PLAYER_1());
let round_id = lyricsflip.create_round(Option::Some(Genre::HipHop), seed);

stop_cheat_caller_address(lyricsflip.contract_address);

start_cheat_caller_address(lyricsflip.contract_address, PLAYER_3());

lyricsflip.join_round(round_id);

stop_cheat_caller_address(lyricsflip.contract_address);

let player_one_stats = lyricsflip.get_player_stat(PLAYER_1());
let player_two_stats = lyricsflip.get_player_stat(PLAYER_2());
let player_three_stats = lyricsflip.get_player_stat(PLAYER_3());

assert(player_one_stats.total_rounds == 0, 'total rounds not zero');
assert(player_two_stats.total_rounds == 0, 'total rounds not zero');
assert(player_three_stats.total_rounds == 0, 'total rounds not zero');

start_cheat_caller_address(lyricsflip.contract_address, PLAYER_1());
lyricsflip.start_round(round_id);
stop_cheat_caller_address(lyricsflip.contract_address);

let curr_player_stats = lyricsflip.get_player_stat(PLAYER_1());
let curr_player_two_stats = lyricsflip.get_player_stat(PLAYER_2());
let curr_player_three_stats = lyricsflip.get_player_stat(PLAYER_3());

assert(curr_player_stats.total_rounds == 1, 'Player stats not updated');
assert(curr_player_two_stats.total_rounds == 0, 'Player two stats updated');
assert(curr_player_three_stats.total_rounds == 1, 'Player three stats not updated');

start_cheat_caller_address(lyricsflip.contract_address, PLAYER_2());

let round_two_id = lyricsflip.create_round(Option::Some(Genre::HipHop), seed);

lyricsflip.start_round(round_two_id);

stop_cheat_caller_address(lyricsflip.contract_address);

let new_player_two_stats = lyricsflip.get_player_stat(PLAYER_2());

assert(new_player_two_stats.total_rounds == 1, 'Player two stats not updated');
}
8 changes: 8 additions & 0 deletions onchain/src/utils/types.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ pub struct Card {
pub lyrics: ByteArray,
}

#[derive(Drop, Serde, starknet::Store)]
pub struct PlayerStats {
pub total_rounds: u64,
pub rounds_won: u64,
pub current_streak: u64,
pub max_streak: u64,
}

#[derive(Drop, Copy, Serde, PartialEq, starknet::Store)]
pub enum Genre {
HipHop,
Expand Down