From 24be9cadbe6bf474184729cf72c8f580a3f786a0 Mon Sep 17 00:00:00 2001 From: Ajidokwu Sabo Date: Thu, 17 Jul 2025 18:43:08 +0100 Subject: [PATCH 1/3] chore: finalized player networth calculation --- src/interfaces/IActions.cairo | 2 + src/systems/actions.cairo | 68 +++++++++++++++++ src/tests/test_world.cairo | 140 ++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) diff --git a/src/interfaces/IActions.cairo b/src/interfaces/IActions.cairo index d32326f..09bfa56 100644 --- a/src/interfaces/IActions.cairo +++ b/src/interfaces/IActions.cairo @@ -45,6 +45,8 @@ pub trait IActions { fn use_getout_of_jail_chance(ref self: T, game_id: u256) -> bool; fn use_getout_of_jail_community_chest(ref self: T, game_id: u256) -> bool; + fn calculate_net_worth(ref self: T, player: GamePlayer) -> u256; + fn offer_trade( ref self: T, game_id: u256, diff --git a/src/systems/actions.cairo b/src/systems/actions.cairo index c277252..d21c538 100644 --- a/src/systems/actions.cairo +++ b/src/systems/actions.cairo @@ -1426,6 +1426,74 @@ pub mod actions { true } + fn calculate_net_worth(ref self: ContractState, player: GamePlayer) -> u256 { + let mut world = self.world_default(); + + let mut total_property_value: u256 = 0; + let mut total_house_cost: u256 = 0; + let mut total_rent_value: u256 = 0; + let mut card_value: u256 = 0; + let mut i = 0; + let properties_len = player.properties_owned.len(); + + while i < properties_len { + let prop_id = *player.properties_owned.at(i); + let game_id = player.game_id; + let property: Property = self.get_property(prop_id, game_id); + + // Property value (half if mortgaged) + if property.is_mortgaged { + total_property_value += property.cost_of_property / 2; + } else { + total_property_value += property.cost_of_property; + } + + // House/hotel cost + if property.development < 5 { + total_house_cost += property.cost_of_house * property.development.into(); + } else if property.development == 5 { + total_house_cost += property.cost_of_house * 5; + } + + // Rent value (always add — mortgaged or not, since it's dev level based) + let rent = match property.development { + 0 => property.rent_site_only, + 1 => property.rent_one_house, + 2 => property.rent_two_houses, + 3 => property.rent_three_houses, + 4 => property.rent_four_houses, + _ => property.rent_hotel, + }; + total_rent_value += rent; + + i += 1; + }; + + // Jail/Chance card value + if player.chance_jail_card { + card_value += 50; + } + if player.comm_free_card { + card_value += 50; + } + + let net_worth = player.balance + + total_property_value + + total_house_cost + + total_rent_value + + card_value; + + // Debug prints + println!("Balance: {}", player.balance); + println!("Total property value: {}", total_property_value); + println!("Total house cost: {}", total_house_cost); + println!("Total rent value: {}", total_rent_value); + println!("Card value: {}", card_value); + println!("NET WORTH: {}", net_worth); + + net_worth + } + fn reject_trade(ref self: ContractState, trade_id: u256, game_id: u256) -> bool { let mut world = self.world_default(); diff --git a/src/tests/test_world.cairo b/src/tests/test_world.cairo index 01a2183..2f4a4c4 100644 --- a/src/tests/test_world.cairo +++ b/src/tests/test_world.cairo @@ -3653,5 +3653,145 @@ mod tests { let trade = actions_system.get_trade(trade_id); assert(trade.status == TradeStatus::Rejected, 'Trade not rejected'); } + + #[test] + fn test_player_net_worth() { + let caller_1 = contract_address_const::<'aji'>(); + let caller_2 = contract_address_const::<'collins'>(); + let caller_3 = contract_address_const::<'jerry'>(); + let caller_4 = contract_address_const::<'aliyu'>(); + let username = 'Ajidokwu'; + let username_1 = 'Collins'; + let username_2 = 'Jerry'; + let username_3 = 'Aliyu'; + + let ndef = namespace_def(); + let mut world = spawn_test_world([ndef].span()); + world.sync_perms_and_inits(contract_defs()); + + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; + + testing::set_contract_address(caller_2); + actions_system.register_new_player(username_1); + + testing::set_contract_address(caller_1); + actions_system.register_new_player(username); + + testing::set_contract_address(caller_3); + actions_system.register_new_player(username_2); + + testing::set_contract_address(caller_4); + actions_system.register_new_player(username_3); + + testing::set_contract_address(caller_1); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); + + testing::set_contract_address(caller_2); + actions_system.join_game(PlayerSymbol::Dog, 1); + + testing::set_contract_address(caller_3); + actions_system.join_game(PlayerSymbol::Car, 1); + + testing::set_contract_address(caller_4); + actions_system.join_game(PlayerSymbol::Iron, 1); + + testing::set_contract_address(caller_1); + let started = actions_system.start_game(1); + assert(started, 'Game start fail'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 1); + let mut property = actions_system.get_property(1, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 12); + + let mut game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_3); + actions_system.move_player(1, 8); + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_4); + actions_system.move_player(1, 8); + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 2); + property = actions_system.get_property(3, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 12); + + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_3); + actions_system.move_player(1, 8); + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_4); + actions_system.move_player(1, 8); + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_1); + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + let success = actions_system.buy_house_or_hotel(property); + assert(success, 'house failed'); + property = actions_system.get_property(3, 1); + assert(property.development == 5, 'dev correct'); + + let aji = actions_system.retrieve_game_player(caller_1, 1); + + assert(aji.total_hotels_owned == 2, 'house count error'); + assert(aji.total_houses_owned == 8, 'house count error'); + + let aji_networth = actions_system.calculate_net_worth(aji); + let collins_networth = actions_system + .calculate_net_worth(actions_system.retrieve_game_player(caller_2, 1)); + let jerry_networth = actions_system + .calculate_net_worth(actions_system.retrieve_game_player(caller_3, 1)); + let ali_networth = actions_system + .calculate_net_worth(actions_system.retrieve_game_player(caller_4, 1)); + println!("aji net worth : {}", aji_networth); + println!("collins net worth : {}", collins_networth); + println!("jerry net worth : {}", jerry_networth); + println!("ali net worth : {}", ali_networth); + } } From 941c2b8d60f78aa580d23d47435f02f39f36bd9d Mon Sep 17 00:00:00 2001 From: Ajidokwu Sabo Date: Thu, 17 Jul 2025 23:57:00 +0100 Subject: [PATCH 2/3] feat: added return winner --- src/interfaces/IActions.cairo | 2 + src/systems/actions.cairo | 22 +++++ src/tests/test_world.cairo | 153 ++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) diff --git a/src/interfaces/IActions.cairo b/src/interfaces/IActions.cairo index 09bfa56..511cdf6 100644 --- a/src/interfaces/IActions.cairo +++ b/src/interfaces/IActions.cairo @@ -47,6 +47,8 @@ pub trait IActions { fn calculate_net_worth(ref self: T, player: GamePlayer) -> u256; + fn get_winner_by_net_worth(ref self: T, players: Array) -> ContractAddress; + fn offer_trade( ref self: T, game_id: u256, diff --git a/src/systems/actions.cairo b/src/systems/actions.cairo index d21c538..bfeb7d4 100644 --- a/src/systems/actions.cairo +++ b/src/systems/actions.cairo @@ -1493,6 +1493,28 @@ pub mod actions { net_worth } + fn get_winner_by_net_worth( + ref self: ContractState, players: Array, + ) -> ContractAddress { + let mut i = 0; + let mut max_net_worth: u256 = 0; + let mut winner_address: ContractAddress = contract_address_const::<'0'>(); + + let players_len = players.len(); + while i < players_len { + let player = players.at(i); + let net_worth = self.calculate_net_worth(player.clone()); + + if net_worth > max_net_worth { + max_net_worth = net_worth; + winner_address = *player.address; + }; + + i += 1; + }; + + winner_address + } fn reject_trade(ref self: ContractState, trade_id: u256, game_id: u256) -> bool { diff --git a/src/tests/test_world.cairo b/src/tests/test_world.cairo index 2f4a4c4..ef85719 100644 --- a/src/tests/test_world.cairo +++ b/src/tests/test_world.cairo @@ -3793,5 +3793,158 @@ mod tests { println!("jerry net worth : {}", jerry_networth); println!("ali net worth : {}", ali_networth); } + + #[test] + fn test_winner() { + let caller_1 = contract_address_const::<'aji'>(); + let caller_2 = contract_address_const::<'collins'>(); + let caller_3 = contract_address_const::<'jerry'>(); + let caller_4 = contract_address_const::<'aliyu'>(); + let username = 'Ajidokwu'; + let username_1 = 'Collins'; + let username_2 = 'Jerry'; + let username_3 = 'Aliyu'; + + let ndef = namespace_def(); + let mut world = spawn_test_world([ndef].span()); + world.sync_perms_and_inits(contract_defs()); + + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; + + testing::set_contract_address(caller_2); + actions_system.register_new_player(username_1); + + testing::set_contract_address(caller_1); + actions_system.register_new_player(username); + + testing::set_contract_address(caller_3); + actions_system.register_new_player(username_2); + + testing::set_contract_address(caller_4); + actions_system.register_new_player(username_3); + + testing::set_contract_address(caller_1); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); + + testing::set_contract_address(caller_2); + actions_system.join_game(PlayerSymbol::Dog, 1); + + testing::set_contract_address(caller_3); + actions_system.join_game(PlayerSymbol::Car, 1); + + testing::set_contract_address(caller_4); + actions_system.join_game(PlayerSymbol::Iron, 1); + + testing::set_contract_address(caller_1); + let started = actions_system.start_game(1); + assert(started, 'Game start fail'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 1); + let mut property = actions_system.get_property(1, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 12); + + let mut game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_3); + actions_system.move_player(1, 8); + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_4); + actions_system.move_player(1, 8); + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 2); + property = actions_system.get_property(3, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 12); + + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_3); + actions_system.move_player(1, 8); + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_4); + actions_system.move_player(1, 8); + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_1); + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + let success = actions_system.buy_house_or_hotel(property); + assert(success, 'house failed'); + property = actions_system.get_property(3, 1); + assert(property.development == 5, 'dev correct'); + + let aji = actions_system.retrieve_game_player(caller_1, 1); + + assert(aji.total_hotels_owned == 2, 'house count error'); + assert(aji.total_houses_owned == 8, 'house count error'); + + let aji_networth = actions_system.calculate_net_worth(aji); + let collins_networth = actions_system + .calculate_net_worth(actions_system.retrieve_game_player(caller_2, 1)); + let jerry_networth = actions_system + .calculate_net_worth(actions_system.retrieve_game_player(caller_3, 1)); + let ali_networth = actions_system + .calculate_net_worth(actions_system.retrieve_game_player(caller_4, 1)); + println!("aji net worth : {}", aji_networth); + println!("collins net worth : {}", collins_networth); + println!("jerry net worth : {}", jerry_networth); + println!("ali net worth : {}", ali_networth); + + let mut players = array![ + actions_system.retrieve_game_player(caller_1, 1), + actions_system.retrieve_game_player(caller_2, 1), + actions_system.retrieve_game_player(caller_3, 1), + actions_system.retrieve_game_player(caller_4, 1), + ]; + + let winner = actions_system.get_winner_by_net_worth(players); + + let winner_felt: felt252 = winner.into(); + println!("Winner is: {}", winner_felt); + assert(winner == caller_1, 'Winner is not Aji'); + } } From 6fea4a6984392616471161d8325f95a428b3e510 Mon Sep 17 00:00:00 2001 From: Ajidokwu Sabo Date: Fri, 18 Jul 2025 00:39:31 +0100 Subject: [PATCH 3/3] feat: added return winner --- src/interfaces/IActions.cairo | 1 + src/model/game_model.cairo | 4 +- src/systems/actions.cairo | 33 +++++++++ src/tests/test_world.cairo | 133 ++++++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 2 deletions(-) diff --git a/src/interfaces/IActions.cairo b/src/interfaces/IActions.cairo index 511cdf6..3efdc83 100644 --- a/src/interfaces/IActions.cairo +++ b/src/interfaces/IActions.cairo @@ -48,6 +48,7 @@ pub trait IActions { fn calculate_net_worth(ref self: T, player: GamePlayer) -> u256; fn get_winner_by_net_worth(ref self: T, players: Array) -> ContractAddress; + fn end_game(ref self: T, game: Game) -> ContractAddress; fn offer_trade( ref self: T, diff --git a/src/model/game_model.cairo b/src/model/game_model.cairo index 99e1c07..f5c8de1 100644 --- a/src/model/game_model.cairo +++ b/src/model/game_model.cairo @@ -31,7 +31,7 @@ pub struct Game { pub status: GameStatus, // Status of the game pub mode: GameType, // Mode of the game pub ready_to_start: bool, // Indicate whether game can be started - pub winner: felt252, // First winner position + pub winner: ContractAddress, // First winner position pub next_player: ContractAddress, // Address of the player to make the next move pub number_of_players: u8, // Number of players in the game pub rolls_count: u256, // Sum of all the numbers rolled by the dice @@ -139,7 +139,7 @@ impl GameImpl of GameTrait { player_boot, player_wheelbarrow, next_player: zero_address.into(), - winner: zero_address.into(), + winner: zero_address, rolls_times: 0, rolls_count: 0, number_of_players, diff --git a/src/systems/actions.cairo b/src/systems/actions.cairo index bfeb7d4..2171325 100644 --- a/src/systems/actions.cairo +++ b/src/systems/actions.cairo @@ -1517,6 +1517,39 @@ pub mod actions { } + fn end_game(ref self: ContractState, game: Game) -> ContractAddress { + let mut world = self.world_default(); + let mut players: Array = ArrayTrait::new(); + + let total_players = game.game_players.len(); + let mut i = 0; + + // Indexed loop over game.players + while i < total_players { + let player_address = game.game_players.at(i); + let player_model: GamePlayer = world.read_model((*player_address, game.id)); + + players.append(player_model); + i += 1; + }; + + // Find the winner by net worth + let winner_address = self.get_winner_by_net_worth(players); + let winner: Player = world.read_model(winner_address); + + // Set game status to ended + let mut updated_game = game; + updated_game.status = GameStatus::Ended; + updated_game.winner = winner.address; + + // Write back the updated game state + world.write_model(@updated_game); + + // Return the winner's address + winner.address + } + + fn reject_trade(ref self: ContractState, trade_id: u256, game_id: u256) -> bool { let mut world = self.world_default(); let caller = get_caller_address(); diff --git a/src/tests/test_world.cairo b/src/tests/test_world.cairo index ef85719..963890a 100644 --- a/src/tests/test_world.cairo +++ b/src/tests/test_world.cairo @@ -3946,5 +3946,138 @@ mod tests { println!("Winner is: {}", winner_felt); assert(winner == caller_1, 'Winner is not Aji'); } + + + #[test] + fn test_end_game() { + let caller_1 = contract_address_const::<'aji'>(); + let caller_2 = contract_address_const::<'collins'>(); + let caller_3 = contract_address_const::<'jerry'>(); + let caller_4 = contract_address_const::<'aliyu'>(); + let username = 'Ajidokwu'; + let username_1 = 'Collins'; + let username_2 = 'Jerry'; + let username_3 = 'Aliyu'; + + let ndef = namespace_def(); + let mut world = spawn_test_world([ndef].span()); + world.sync_perms_and_inits(contract_defs()); + + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; + + testing::set_contract_address(caller_2); + actions_system.register_new_player(username_1); + + testing::set_contract_address(caller_1); + actions_system.register_new_player(username); + + testing::set_contract_address(caller_3); + actions_system.register_new_player(username_2); + + testing::set_contract_address(caller_4); + actions_system.register_new_player(username_3); + + testing::set_contract_address(caller_1); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); + + testing::set_contract_address(caller_2); + actions_system.join_game(PlayerSymbol::Dog, 1); + + testing::set_contract_address(caller_3); + actions_system.join_game(PlayerSymbol::Car, 1); + + testing::set_contract_address(caller_4); + actions_system.join_game(PlayerSymbol::Iron, 1); + + testing::set_contract_address(caller_1); + let started = actions_system.start_game(1); + assert(started, 'Game start fail'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 1); + let mut property = actions_system.get_property(1, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 12); + + let mut game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_3); + actions_system.move_player(1, 8); + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_4); + actions_system.move_player(1, 8); + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 2); + property = actions_system.get_property(3, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 12); + + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_3); + actions_system.move_player(1, 8); + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_4); + actions_system.move_player(1, 8); + game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_1); + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + + property = actions_system.get_property(1, 1); + let success = actions_system.buy_house_or_hotel(property); + assert(success, 'house failed'); + property = actions_system.get_property(3, 1); + assert(property.development == 5, 'dev correct'); + + let mut game = actions_system.retrieve_game(1); + + let winner = actions_system.end_game(game.clone()); + game = actions_system.retrieve_game(1); + let winner_felt: felt252 = winner.into(); + println!("Winner is: {}", winner_felt); + assert(winner == caller_1, 'Winner is not Aji'); + assert(game.status == GameStatus::Ended, 'Game not finished'); + } }