diff --git a/src/interfaces/IActions.cairo b/src/interfaces/IActions.cairo index f932f42..701e057 100644 --- a/src/interfaces/IActions.cairo +++ b/src/interfaces/IActions.cairo @@ -42,10 +42,13 @@ pub trait IActions { fn get_community_chest(self: @T, id: u8, game_id: u256) -> CommunityChest; fn get_railroad(self: @T, id: u8, game_id: u256) -> RailRoad; fn get_tax(self: @T, id: u8, game_id: u256) -> Tax; + 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; // Dice & player movement fn roll_dice(ref self: T) -> (u8, u8); fn move_player(ref self: T, game_id: u256, steps: u8) -> u8; + fn pay_jail_fine(ref self: T, game_id: u256) -> bool; // fn handle_chance(ref self: T, game_id: u256, random_index: u32) -> @ByteArray; // Handling landings on board diff --git a/src/model/game_player_model.cairo b/src/model/game_player_model.cairo index f0cc122..91685c7 100644 --- a/src/model/game_player_model.cairo +++ b/src/model/game_player_model.cairo @@ -30,6 +30,7 @@ pub struct GamePlayer { pub no_section8: u8, pub is_bankrupt: bool, pub is_active: bool, + pub jail_turns: u8, } @@ -78,6 +79,7 @@ impl GamePlayerImpl of GamePlayerTrait { no_section6: 0, no_section7: 0, no_section8: 0, + jail_turns: 0, } } diff --git a/src/systems/actions.cairo b/src/systems/actions.cairo index 45f3a59..34f2ee4 100644 --- a/src/systems/actions.cairo +++ b/src/systems/actions.cairo @@ -463,6 +463,63 @@ pub mod actions { true } + fn pay_jail_fine(ref self: ContractState, game_id: u256) -> bool { + let mut world = self.world_default(); + let caller = get_caller_address(); + let mut player: GamePlayer = world.read_model((caller, game_id)); + let mut game: Game = world.read_model(game_id); + + assert(game.status == GameStatus::Ongoing, 'Game not started'); + assert(player.jailed, 'Not in jail'); + + // Pay the fine + let fine_amount: u256 = 50; + assert(player.balance >= fine_amount, 'Insufficient funds to pay fine'); + + player.balance -= fine_amount; + player.jailed = false; + player.jail_turns = 0; + + world.write_model(@game); + world.write_model(@player); + + true + } + + fn use_getout_of_jail_chance(ref self: ContractState, game_id: u256) -> bool { + let mut world = self.world_default(); + let caller = get_caller_address(); + let mut player: GamePlayer = world.read_model((caller, game_id)); + let mut game: Game = world.read_model(game_id); + assert(player.chance_jail_card, 'No chance card'); + assert(player.jailed, 'Not in jail'); + assert(game.status == GameStatus::Ongoing, 'Game not started'); + // Use the card + player.chance_jail_card = false; + player.jailed = false; + player.jail_turns = 0; + world.write_model(@game); + world.write_model(@player); + true + } + + fn use_getout_of_jail_community_chest(ref self: ContractState, game_id: u256) -> bool { + let mut world = self.world_default(); + let caller = get_caller_address(); + let mut player: GamePlayer = world.read_model((caller, game_id)); + let mut game: Game = world.read_model(game_id); + assert(player.comm_free_card, 'No community chest card'); + assert(player.jailed, 'Not in jail'); + assert(game.status == GameStatus::Ongoing, 'Game not started'); + // Use the card + player.comm_free_card = false; + player.jailed = false; + player.jail_turns = 0; + world.write_model(@game); + world.write_model(@player); + true + } + fn move_player(ref self: ContractState, game_id: u256, steps: u8) -> u8 { let mut world = self.world_default(); @@ -470,29 +527,58 @@ pub mod actions { let mut game_player: GamePlayer = world.read_model((caller, game_id)); let mut game: Game = world.read_model(game_id); - assert(game.next_player == caller, 'Not your turn'); - assert(game.status == GameStatus::Ongoing, 'Game is not ongoing'); + assert!(game.next_player == caller, "Not your turn"); + assert!(game.status == GameStatus::Ongoing, "Game is not ongoing"); - // Move player - game_player = GamePlayerTrait::move(game_player, steps); + // Handle jailed players + if game_player.jailed { + game_player.jail_turns += 1; + + if game_player.jail_turns > 3 { + // Automatically release player after 3 turns + game_player.jailed = false; + game_player.jail_turns = 0; + } else { + // Still in jail, no move + world.write_model(@game_player); + return game_player.position; + } + } + // Now free to move + game_player = GamePlayerTrait::move(game_player, steps); game_player.dice_rolled = steps; + // Passed or landed on Go if game_player.position >= 40 { game_player.position %= 40; game_player.balance += 200; } + // Landing on "Go To Jail" space + if game_player.position == 30 { + game_player.position = 10; + game_player.jailed = true; + game_player.jail_turns = 0; + + world.write_model(@game_player); + world.write_model(@game); + return game_player.position; + } + + // Handle landing on property let mut property = self.get_property(game_player.position, game_id); - property = self.handle_property_landing(game_player.clone(), property.clone()); + property = self.handle_property_landing(game_player.clone(), property); // Update state world.write_model(@game_player); world.write_model(@game); + world.write_model(@property); game_player.position } + fn buy_property(ref self: ContractState, mut property: Property) -> bool { // get the world let mut world = self.world_default(); @@ -927,7 +1013,7 @@ pub mod actions { property.property_type == PropertyType::CommunityChest, 'not on community chest', ); if card == "Advance to Go (Collect $200)" { - player.position = 1; + player.position = 0; player.balance += 200; } else if card == "Bank error in your favor - Collect $200" { player.balance += 200; @@ -938,7 +1024,7 @@ pub mod actions { } else if card == "Get Out of Jail Free" { player.comm_free_card = true; } else if card == "Go to Jail" { - player.position = 11; // jail position + player.position = 10; // jail position player.jailed = true; } else if card == "Grand Opera Night - collect $50 from every player" { let mut i = 0; diff --git a/src/tests/test_world.cairo b/src/tests/test_world.cairo index 222511c..1d4bb8a 100644 --- a/src/tests/test_world.cairo +++ b/src/tests/test_world.cairo @@ -2159,5 +2159,509 @@ mod tests { assert(ply.position == 2, 'position error'); assert(ply.balance == 1450, 'bal error'); } + + #[test] + fn test_process_community_chest_individually() { + let caller_1 = contract_address_const::<'aji'>(); + let caller_2 = contract_address_const::<'collins'>(); + + let username = 'Ajidokwu'; + let username_1 = 'Collins'; + + 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_1); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 2); + + testing::set_contract_address(caller_2); + actions_system.join_game(PlayerSymbol::Dog, 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, 2); + + let mut g = actions_system.retrieve_game(1); + let mut p = actions_system.retrieve_game_player(caller_1, 1); + + let mut community_chest: ByteArray = "Advance to Go (Collect $200)"; + + let (_, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + assert(ply.position == 0, 'position error'); + assert(ply.balance == 1700, 'bal error'); + + g = actions_system.finish_turn(g); + testing::set_contract_address(caller_2); + actions_system.move_player(1, 2); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + community_chest = "Bank error in your favor - Collect $200"; + + let (_, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + g = actions_system.finish_turn(g); + + assert(ply.position == 2, 'position error'); + assert(ply.balance == 1700, 'bal error'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 2); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + community_chest = "Bank error in your favor - Collect $200"; + + let (_, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 2, 'position error'); + assert(ply.balance == 1900, 'bal error'); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 15); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + community_chest = "Doctor fee - Pay $50"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 17, 'position error'); + assert(ply.balance == 1650, 'bal error'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 15); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + community_chest = "From sale of stock - collect $50"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 17, 'position error'); + assert(ply.balance == 1950, 'bal error'); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 16); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + community_chest = "Get Out of Jail Free"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 33, 'position error'); + assert(ply.balance == 1650, 'bal error'); + assert(ply.comm_free_card, 'jail card error'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 16); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + community_chest = "Grand Opera Night - collect $50 from every player"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 33, 'position error'); + assert(ply.balance == 2000, 'bal error'); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 9); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + community_chest = "Go to Jail"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 10, 'position error'); + assert(ply.balance == 1800, 'bal error'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 9); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + community_chest = "Holiday Fund matures - Receive $100"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 2, 'position error'); + assert(ply.balance == 2300, 'bal error'); + + testing::set_contract_address(caller_2); + actions_system.use_getout_of_jail_community_chest(1); + actions_system.move_player(1, 7); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + community_chest = "Income tax refund - Collect $20"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 17, 'position error'); + assert(ply.balance == 1820, 'bal error'); + + // // // HERE + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 15); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + community_chest = "Life insurance matures - Collect $100"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 17, 'position error'); + assert(ply.balance == 2400, 'bal error'); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 16); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + community_chest = "Pay hospital fees of $100"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 33, 'position error'); + assert(ply.balance == 1720, 'bal error'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 16); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + community_chest = "Pay school fees of $150"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 33, 'position error'); + assert(ply.balance == 2250, 'bal error'); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 9); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + community_chest = "Street repairs - $40 per house, $115 per hotel"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 2, 'position error'); + assert(ply.balance == 1920, 'bal error'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 9); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + community_chest = "Won second prize in beauty contest - Collect $10"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 2, 'position error'); + assert(ply.balance == 2460, 'bal error'); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 15); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + community_chest = "You inherit $100"; + + let (g, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + actions_system.finish_turn(g); + + assert(ply.position == 17, 'position error'); + assert(ply.balance == 2020, 'bal error'); + } + + #[test] + fn test_going_jail_and_using_community_and_chance_cards() { + let caller_1 = contract_address_const::<'aji'>(); + let caller_2 = contract_address_const::<'collins'>(); + + let username = 'Ajidokwu'; + let username_1 = 'Collins'; + + 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_1); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 2); + + testing::set_contract_address(caller_2); + actions_system.join_game(PlayerSymbol::Dog, 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, 2); + + let mut g = actions_system.retrieve_game(1); + let mut p = actions_system.retrieve_game_player(caller_1, 1); + + let mut community_chest: ByteArray = "Get Out of Jail Free"; + + let (_, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + g = actions_system.finish_turn(g); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 7); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + let chance = "Get out of Jail Free"; + + let (_, ply1) = actions_system.process_chance_card(g.clone(), p, chance); + g = actions_system.finish_turn(g); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 5); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + let chance = "Go to Jail"; + + let (_, ply) = actions_system.process_chance_card(g.clone(), p, chance); + g = actions_system.finish_turn(g); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 10); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + let chance = "Go to Jail"; + + let (game, ply1) = actions_system.process_community_chest_card(g.clone(), p, chance); + g = actions_system.finish_turn(g); + + testing::set_contract_address(caller_1); + actions_system.use_getout_of_jail_community_chest(game.id); + actions_system.move_player(1, 5); + g = actions_system.finish_turn(g); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + testing::set_contract_address(caller_2); + actions_system.use_getout_of_jail_chance(game.id); + actions_system.move_player(1, 10); + g = actions_system.finish_turn(g); + + g = actions_system.retrieve_game(1); + let pl = actions_system.retrieve_game_player(caller_2, 1); + + assert!(p.jail_turns == 0, "p jail_turns not zero"); + assert!(!p.jailed, "p still jailed"); + assert!(!p.chance_jail_card, "p still has chance card"); + assert!(!p.comm_free_card, "p still has community card"); + + assert!(pl.jail_turns == 0, "pl jail_turns not zero"); + assert!(!pl.jailed, "pl still jailed"); + assert!(!pl.chance_jail_card, "pl still has chance card"); + assert!(!pl.comm_free_card, "pl still has community card"); + } + + #[test] + fn test_going_to_jail_pay_fine_and_miss_3_turns() { + let caller_1 = contract_address_const::<'aji'>(); + let caller_2 = contract_address_const::<'collins'>(); + + let username = 'Ajidokwu'; + let username_1 = 'Collins'; + + 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_1); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 2); + + testing::set_contract_address(caller_2); + actions_system.join_game(PlayerSymbol::Dog, 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, 2); + + let mut g = actions_system.retrieve_game(1); + let mut p = actions_system.retrieve_game_player(caller_1, 1); + + let mut community_chest: ByteArray = "Get Out of Jail Free"; + + let (_, ply) = actions_system.process_community_chest_card(g.clone(), p, community_chest); + g = actions_system.finish_turn(g); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 7); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + let chance = "Get out of Jail Free"; + + let (_, ply1) = actions_system.process_chance_card(g.clone(), p, chance); + g = actions_system.finish_turn(g); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 5); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + let chance = "Go to Jail"; + + let (_, ply) = actions_system.process_chance_card(g.clone(), p, chance); + g = actions_system.finish_turn(g); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 10); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + let chance = "Go to Jail"; + + let (game, ply1) = actions_system.process_community_chest_card(g.clone(), p, chance); + g = actions_system.finish_turn(g); + + testing::set_contract_address(caller_1); + actions_system.pay_jail_fine(game.id); + actions_system.move_player(1, 5); + g = actions_system.finish_turn(g); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + assert!(p.jail_turns == 0, "p jail_turns not zero"); + assert!(!p.jailed, "p still jailed"); + assert!(p.balance == 1450, "pl balance error"); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 10); + g = actions_system.finish_turn(g); + + g = actions_system.retrieve_game(1); + let pl = actions_system.retrieve_game_player(caller_2, 1); + + testing::set_contract_address(caller_1); + + actions_system.move_player(1, 5); + g = actions_system.finish_turn(g); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 10); + g = actions_system.finish_turn(g); + + g = actions_system.retrieve_game(1); + let pl = actions_system.retrieve_game_player(caller_2, 1); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 10); + g = actions_system.finish_turn(g); + + testing::set_contract_address(caller_2); + + actions_system.move_player(1, 5); + g = actions_system.finish_turn(g); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 10); + g = actions_system.finish_turn(g); + + testing::set_contract_address(caller_2); + + actions_system.move_player(1, 5); + g = actions_system.finish_turn(g); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + g = actions_system.retrieve_game(1); + let pl = actions_system.retrieve_game_player(caller_2, 1); + + assert!(pl.jail_turns == 0, "pl.jail_turns not zero"); + assert!(pl.balance == 1500, "pl.balance not 1500"); + assert!(!pl.jailed, "pl is still jailed"); + assert!(pl.chance_jail_card, "pl does not have chance jail card"); + assert!(!pl.comm_free_card, "pl still has community jail card"); + assert!(pl.position == 15, "pl.position not 15"); + } }