diff --git a/.tool-versions b/.tool-versions index 87b9094..aa00807 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -dojo 1.2.2 scarb 2.9.2 +dojo 1.2.2 \ No newline at end of file diff --git a/Scarb.toml b/Scarb.toml index f42ea3c..ea0842e 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -16,6 +16,8 @@ move = "sozo execute dojo_starter-actions move -c 1 --wait" # scarb run move dojo = { git = "https://github.com/dojoengine/dojo", tag = "v1.2.2" } origami_random = { git = "https://github.com/dojoengine/origami" } + + [[target.starknet-contract]] build-external-contracts = ["dojo::world::world_contract::world"] diff --git a/configs/properties.json b/configs/properties.json new file mode 100644 index 0000000..1e702d8 --- /dev/null +++ b/configs/properties.json @@ -0,0 +1,543 @@ +{ + "properties": [ + { + "name": "Axone Avenue", + "id": "axoneave", + "position": 1, + "price": 60, + "rent": 2, + "multpliedrent": [10, 30, 90, 160, 250], + "housecost": 50, + "group": "Purple" + }, + { + "name": "Onlydust Avenue", + "id": "onlydustave", + "position": 3, + "price": 60, + "rent": 4, + "multpliedrent": [20, 60, 180, 320, 450], + "housecost": 50, + "group": "Purple" + }, + { + "name": "ZkSync Lane", + "id": "zksynclane", + "position": 6, + "price": 100, + "rent": 6, + "multpliedrent": [30, 90, 270, 400, 550], + "housecost": 50, + "group": "lightgreen" + }, + { + "name": "Starknet Lane", + "id": "starknetlane", + "position": 8, + "price": 100, + "rent": 6, + "multpliedrent": [30, 90, 270, 400, 550], + "housecost": 50, + "group": "lightgreen" + }, + { + "name": "Linea Lane", + "id": "linealane", + "position": 9, + "price": 120, + "rent": 8, + "multpliedrent": [40, 100, 300, 450, 600], + "housecost": 50, + "group": "lightgreen" + }, + { + "name": "Arbitrium Avenue", + "id": "arbitriumave", + "position": 11, + "price": 140, + "rent": 10, + "multpliedrent": [50, 150, 450, 625, 750], + "housecost": 100, + "group": "Violet" + }, + { + "name": "Optimistic Avenue", + "id": "optimisticave", + "position": 13, + "price": 140, + "rent": 10, + "multpliedrent": [50, 150, 450, 625, 750], + "housecost": 100, + "group": "Violet" + }, + { + "name": "Base Avenue", + "id": "baseave", + "position": 14, + "price": 160, + "rent": 12, + "multpliedrent": [60, 180, 500, 700, 900], + "housecost": 100, + "group": "Violet" + }, + { + "name": "Cosmos Lane", + "id": "cosmosave", + "position": 16, + "price": 180, + "rent": 14, + "multpliedrent": [70, 200, 550, 750, 950], + "housecost": 100, + "group": "Orange" + }, + { + "name": "Polkadot Lane", + "id": "polkadotlane", + "position": 18, + "price": 180, + "rent": 14, + "multpliedrent": [70, 200, 550, 750, 950], + "housecost": 100, + "group": "Orange" + }, + { + "name": "Near Lane", + "id": "nearlane", + "position": 19, + "price": 200, + "rent": 16, + "multpliedrent": [80, 220, 600, 800, 1000], + "housecost": 100, + "group": "Orange" + }, + { + "name": "Uniswap Avenue", + "id": "uniswapave", + "position": 21, + "price": 220, + "rent": 18, + "multpliedrent": [90, 250, 700, 875, 1050], + "housecost": 150, + "group": "Red" + }, + { + "name": "MakerDAO Avenue", + "id": "makerdaoave", + "position": 23, + "price": 220, + "rent": 18, + "multpliedrent": [90, 250, 700, 875, 1050], + "housecost": 150, + "group": "Red" + }, + { + "name": "Aave Avenue", + "id": "aaveave", + "position": 24, + "price": 240, + "rent": 20, + "multpliedrent": [100, 300, 750, 925, 1100], + "housecost": 150, + "group": "Red" + }, + { + "name": "Lisk Lane", + "id": "liskave", + "position": 26, + "price": 260, + "rent": 22, + "multpliedrent": [110, 330, 800, 975, 1150], + "housecost": 150, + "group": "Yellow" + }, + { + "name": "Rootstock Lane", + "id": "rootstockave", + "position": 27, + "price": 260, + "rent": 22, + "multpliedrent": [110, 330, 800, 975, 1150], + "housecost": 150, + "group": "Yellow" + }, + { + "name": "Ark Lane", + "id": "arkave", + "position": 29, + "price": 280, + "rent": 22, + "multpliedrent": [120, 360, 850, 1025, 1200], + "housecost": 150, + "group": "Yellow" + }, + { + "name": "Avalanche Avenue", + "id": "avalancheave", + "position": 31, + "price": 300, + "rent": 26, + "multpliedrent": [130, 390, 900, 1100, 1275], + "housecost": 200, + "group": "darkgreen" + }, + { + "name": "Solana Drive", + "id": "solanadrive", + "position": 32, + "price": 300, + "rent": 26, + "multpliedrent": [130, 390, 900, 1100, 1275], + "housecost": 200, + "group": "darkgreen" + }, + { + "name": "Ethereum Avenue", + "id": "ethereumave", + "position": 34, + "price": 320, + "rent": 28, + "multpliedrent": [150, 450, 1000, 1200, 1400], + "housecost": 200, + "group": "darkgreen" + }, + { + "name": "CoreDao Park", + "id": "coredaopark", + "position": 37, + "price": 350, + "rent": 35, + "multpliedrent": [175, 500, 1100, 1300, 1500], + "housecost": 200, + "group": "darkblue" + }, + { + "name": "Bitcoin Lane", + "id": "bitcoinlane", + "position": 39, + "price": 400, + "rent": 50, + "multpliedrent": [200, 600, 1400, 1700, 2000], + "housecost": 200, + "group": "darkblue" + }, + { + "name": "The Graph", + "id": "thegraph", + "position": 12, + "price": 150, + "group": "Utilities" + }, + { + "name": "Chainlink", + "id": "chainlink", + "position": 28, + "price": 150, + "group": "Utilities" + }, + { + "name": "IPFS", + "id": "ipfs", + "position": 5, + "price": 200, + "group": "Railroad" + }, + { + "name": "Pinata", + "id": "pinata", + "position": 15, + "price": 200, + "group": "Railroad" + }, + { + "name": "Open Zeppelin", + "id": "openzeppelin", + "position": 25, + "price": 200, + "group": "Railroad" + }, + { + "name": "Cartridge Railroad", + "id": "cartridge", + "position": 35, + "price": 200, + "group": "Railroad" + }, + { + "name": "Go", + "id": "go", + "position": 0, + "group": "Special-ex" + }, + { + "name": "Income Tax", + "id": "incometax", + "position": 4, + "group": "Special" + }, + { + "name": "Jail / Just Visiting", + "id": "jail", + "position": 10, + "group": "Special-ex" + }, + { + "name": "Chance", + "id": "chance", + "group": "Special", + "position": 36 + }, + { + "name": "Chance", + "id": "chance", + "group": "Special", + "position": 7 + }, + { + "name": "Chance", + "id": "chance", + "group": "Special", + "position": 22 + }, + { + "name": "Free Parking", + "id": "freeparking", + "group": "Special-ex", + "position": 20 + }, + { + "name": "Community Chest", + "id": "communitychest", + "group": "Special", + "position": 2 + }, + { + "name": "Community Chest", + "id": "communitychest", + "group": "Special", + "position": 33 + }, + { + "name": "Community Chest", + "id": "communitychest", + "group": "Special", + "position": 17 + }, + { + "name": "Go To Jail", + "id": "gotojail", + "group": "Special-ex", + "position": 30 + }, + { + "name": "Luxury Tax", + "id": "luxerytax", + "group": "Special", + "position": 38 + } + ], + "tiles": [ + { "id": "go" }, + { "id": "axoneave" }, + { "id": "communitychest" }, + { "id": "onlydustave" }, + { "id": "incometax" }, + { "id": "ipfs" }, + { "id": "zksynclane" }, + { "id": "chance" }, + { "id": "starknetlane" }, + { "id": "linealane" }, + { "id": "jail" }, + { "id": "arbitriumave" }, + { "id": "thegraph" }, + { "id": "optimisticave" }, + { "id": "baseave" }, + { "id": "pinata" }, + { "id": "cosmosave" }, + { "id": "communitychest" }, + { "id": "polkadotlane" }, + { "id": "nearlane" }, + { "id": "freeparking" }, + { "id": "uniswapave" }, + { "id": "chance" }, + { "id": "makerdaoave" }, + { "id": "aaveave" }, + { "id": "openzeppelin" }, + { "id": "liskave" }, + { "id": "rootstockave" }, + { "id": "chainlink" }, + { "id": "arkave" }, + { "id": "gotojail" }, + { "id": "avalancheave" }, + { "id": "solanadrive" }, + { "id": "communitychest" }, + { "id": "ethereumave" }, + { "id": "cartridge" }, + { "id": "chance" }, + { "id": "coredaopark" }, + { "id": "luxerytax" }, + { "id": "bitcoinlane" } + ], + "chance": [ + { + "title": "Advance to Go (Collect $200)", + "action": "move", + "tileid": "go" + }, + { + "title": "Advance to Illinois Avenue - If you pass Go, collect $200", + "action": "move", + "tileid": "illinoisave" + }, + { + "title": "Advance to St. Charles Place - If you pass Go, collect $200", + "action": "move", + "tileid": "stcharlesplace" + }, + { + "title": "Advance token to nearest Utility. If unowned, you may buy it from the Bank. If owned, throw dice and pay owner a total ten times the amount thrown.", + "action": "movenearest", + "groupid": "utility", + "rentmultiplier": 10 + }, + { + "title": "Advance token to the nearest Railroad and pay owner twice the rental to which he/she is otherwise entitled. If Railroad is unowned, you may buy it from the Bank.", + "action": "movenearest", + "groupid": "railroad", + "rentmultiplier": 2 + }, + { + "title": "Bank pays you dividend of $50", + "action": "addfunds", + "amount": 50 + }, + { + "title": "Get out of Jail Free - This card may be kept until needed, or traded/sold", + "action": "jail", + "subaction": "getout" + }, + { + "title": "Go Back 3 Spaces", + "action": "move", + "count": -3 + }, + { + "title": "Go to Jail - Go directly to Jail - Do not pass Go, do not collect $200", + "action": "jail", + "subaction": "goto" + }, + { + "title": "Make general repairs on all your property - For each house pay $25 - For each hotel $100", + "action": "propertycharges", + "buildings": 25, + "hotels": 100 + }, + { + "title": "Pay poor tax of $15", + "action": "removefunds", + "amount": 15 + }, + { + "title": "Take a trip to Reading Railroad - If you pass Go, collect $200", + "action": "move", + "tileid": "readingrailroad" + }, + { + "title": "Take a walk on the Boardwalk - Advance token to Boardwalk", + "action": "move", + "tileid": "boardwalk" + }, + { + "title": "You have been elected Chairman of the Board - Pay each player $50", + "action": "removefundstoplayers", + "amount": 50 + }, + { + "title": "Your building loan matures - Collect $150", + "action": "addfunds", + "amount": 50 + } + ], + "communitychest": [ + { + "title": "Advance to Go (Collect $200)", + "action": "move", + "tileid": "go" + }, + { + "title": "Bank error in your favor - Collect $200 ", + "action": "addfunds", + "amount": 200 + }, + { + "title": "Doctor fee - Pay $50", + "action": "removefunds", + "amount": 50 + }, + { + "title": "From sale of stock you get $50", + "action": "addfunds", + "amount": 50 + }, + { + "title": "Get Out of Jail Free", + "action": "jail", + "subaction": "getout" + }, + { + "title": "Go to Jail - Go directly to jail - Do not pass Go - Do not collect $200", + "action": "jail", + "subaction": "goto" + }, + { + "title": "Grand Opera Night - Collect $50 from every player for opening night seats", + "action": "addfundsfromplayers", + "amount": 50 + }, + { + "title": "Holiday Fund matures - Receive $100", + "action": "addfunds", + "amount": 100 + }, + { + "title": "Income tax refund - Collect $20", + "action": "addfunds", + "amount": 20 + }, + { + "title": "Life insurance matures - Collect $100", + "action": "addfunds", + "amount": 100 + }, + { + "title": "Pay hospital fees of $100", + "action": "removefunds", + "amount": 100 + }, + { + "title": "Pay school fees of $150", + "action": "removefunds", + "amount": 150 + }, + { + "title": "Receive $25 consultancy fee", + "action": "addfunds", + "amount": 25 + }, + { + "title": "You are assessed for street repairs - $40 per house - $115 per hotel", + "action": "propertycharges", + "buildings": 40, + "hotels": 115 + }, + { + "title": "You have won second prize in a beauty contest - Collect $10", + "action": "addfunds", + "amount": 10 + }, + { + "title": "You inherit $100", + "action": "addfunds", + "amount": 100 + } + ] +} \ No newline at end of file diff --git a/src/interfaces/IActions.cairo b/src/interfaces/IActions.cairo new file mode 100644 index 0000000..f932f42 --- /dev/null +++ b/src/interfaces/IActions.cairo @@ -0,0 +1,106 @@ +use dojo_starter::model::game_model::{GameType, Game}; +use dojo_starter::model::game_player_model::{PlayerSymbol, GamePlayer}; +use dojo_starter::model::player_model::Player; +use dojo_starter::model::property_model::Property; +use dojo_starter::model::utility_model::Utility; +use dojo_starter::model::rail_road_model::RailRoad; +use dojo_starter::model::community_chest_model::CommunityChest; +use dojo_starter::model::chance_model::Chance; +use dojo_starter::model::jail_model::Jail; +use dojo_starter::model::go_free_parking_model::Go; +use dojo_starter::model::tax_model::Tax; +use starknet::{ContractAddress}; + + +#[starknet::interface] +pub trait IActions { + // Player registration & account + fn register_new_player(ref self: T, username: felt252); + fn is_registered(self: @T, address: ContractAddress) -> bool; + fn get_username_from_address(self: @T, address: ContractAddress) -> felt252; + fn retrieve_player(self: @T, addr: ContractAddress) -> Player; + + // Game creation & joining + fn create_new_game( + ref self: T, game_type: GameType, player_symbol: PlayerSymbol, number_of_players: u8, + ) -> u256; + fn join_game(ref self: T, player_symbol: PlayerSymbol, game_id: u256); + fn start_game(ref self: T, game_id: u256) -> bool; + fn finish_turn(ref self: T, game: Game) -> Game; + + // Game state retrieval + fn retrieve_game(self: @T, game_id: u256) -> Game; + fn retrieve_game_player(self: @T, addr: ContractAddress, game_id: u256) -> GamePlayer; + fn is_game_started(self: @T, game_id: u256) -> u8; + + // Board spaces retrieval + fn get_property(self: @T, id: u8, game_id: u256) -> Property; + fn get_utility(self: @T, id: u8, game_id: u256) -> Utility; + fn get_chance(self: @T, id: u8, game_id: u256) -> Chance; + fn get_jail(self: @T, id: u8, game_id: u256) -> Jail; + fn get_go(self: @T, id: u8, game_id: u256) -> Go; + 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; + + // Dice & player movement + fn roll_dice(ref self: T) -> (u8, u8); + fn move_player(ref self: T, game_id: u256, steps: u8) -> u8; + // fn handle_chance(ref self: T, game_id: u256, random_index: u32) -> @ByteArray; + + // Handling landings on board + // fn draw_chance_card(ref self: T, game_id: u256) -> Chance; + // fn draw_community_chest_card(ref self: T, game_id: u256) -> CommunityChest; + // fn pay_tax(ref self: T, game_id: u256, tax_id: u8) -> bool; + // fn go_to_jail(ref self: T, game_id: u256) -> bool; + + // Jail specific actions + // fn pay_jail_fee(ref self: T, game_id: u256) -> bool; + // fn use_jail_card(ref self: T, game_id: u256) -> bool; + + // Property transactions + fn buy_property(ref self: T, property: Property) -> bool; + fn sell_property(ref self: T, property_id: u8, game_id: u256) -> bool; + fn mortgage_property(ref self: T, property: Property) -> bool; + fn unmortgage_property(ref self: T, property: Property) -> bool; + fn pay_rent(ref self: T, property: Property) -> bool; + fn buy_house_or_hotel(ref self: T, property: Property) -> bool; + fn sell_house_or_hotel(ref self: T, property_id: u8, game_id: u256) -> bool; + + fn handle_community_chest(ref self: T, game_id: u256, random_index: u32) -> ByteArray; + fn handle_chance(ref self: T, game_id: u256, random_index: u32) -> ByteArray; + fn process_chance_card( + ref self: T, game: Game, player: GamePlayer, card: ByteArray, + ) -> (Game, GamePlayer); + + fn process_community_chest_card( + ref self: T, game: Game, player: GamePlayer, card: ByteArray, + ) -> (Game, GamePlayer); + // Trading system + // fn offer_trade( + // ref self: T, + // game_id: u256, + // to: ContractAddress, + // offered_property_ids: Array, + // requested_property_ids: Array, + // cash_offer: u256, + // cash_request: u256 + // ); + // fn accept_trade(ref self: T, game_id: u256, trade_id: u256) -> bool; + // fn decline_trade(ref self: T, game_id: u256, trade_id: u256) -> bool; + + // // Auctions + // fn start_auction(ref self: T, property_id: u8, game_id: u256); + // fn bid_on_property(ref self: T, game_id: u256, bid_amount: u256); + + // Player balance & payments + fn get_players_balance(self: @T, player: ContractAddress, game_id: u256) -> u256; + fn transfer_from( + ref self: T, from: GamePlayer, to: GamePlayer, amount: u256, + ) -> Array; + fn mint(ref self: T, recepient: ContractAddress, game_id: u256, amount: u256); + // Bankruptcy & ending game +// fn declare_bankruptcy(ref self: T, game_id: u256) -> bool; +// fn check_winner(self: @T, game_id: u256) -> Option; +// fn end_game(ref self: T, game_id: u256) -> bool; +} diff --git a/src/interfaces/IBlockopoly.cairo b/src/interfaces/IBlockopoly.cairo new file mode 100644 index 0000000..ffaf0bc --- /dev/null +++ b/src/interfaces/IBlockopoly.cairo @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +use starknet::ContractAddress; + +#[starknet::interface] +pub trait IBlockopoly { + fn init(ref self: ContractState, owner: ContractAddress); + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256); + fn get_balance(ref self: ContractState, address: ContractAddress) -> u256; + fn transferFrom( + ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256, + ) -> bool; + fn get_symbol(ref self: ContractState) -> ByteArray; + fn get_name(ref self: ContractState) -> ByteArray; + fn get_allowance( + ref self: ContractState, owner: ContractAddress, spender: ContractAddress, + ) -> u256; + fn approve_user(ref self: ContractState, spender: ContractAddress, amount: u256); +} diff --git a/src/interfaces/IBlockopolyNFT.cairo b/src/interfaces/IBlockopolyNFT.cairo new file mode 100644 index 0000000..c70f1f5 --- /dev/null +++ b/src/interfaces/IBlockopolyNFT.cairo @@ -0,0 +1,6 @@ +use starknet::ContractAddress; + +#[starknet::interface] +pub trait IBlockopolyNFT { + fn mint(ref self: TContractState, recipient: ContractAddress, token_id: u256); +} diff --git a/src/interfaces/IWorld.cairo b/src/interfaces/IWorld.cairo deleted file mode 100644 index 10a7a38..0000000 --- a/src/interfaces/IWorld.cairo +++ /dev/null @@ -1,71 +0,0 @@ -use dojo_starter::model::game_model::{GameMode, Game}; -use dojo_starter::model::player_model::{PlayerSymbol, Player}; -use dojo_starter::model::property_model::{Property}; -use core::array::Array; -use starknet::{ContractAddress}; - -// define the interface -#[starknet::interface] -pub trait IWorld { - fn register_new_player( - ref self: T, - username: felt252, - is_bot: bool, - player_symbol: PlayerSymbol, - initial_balance: u256, - ); - fn get_username_from_address(self: @T, address: ContractAddress) -> felt252; - fn retrieve_player(ref self: T, addr: ContractAddress) -> Player; - fn create_new_game( - ref self: T, game_mode: GameMode, player_symbol: PlayerSymbol, number_of_players: u8, - ) -> u256; - fn create_new_game_id(ref self: T) -> u256; - fn retrieve_game(ref self: T, game_id: u256) -> Game; - fn generate_properties( - ref self: T, - id: u8, - game_id: u256, - name: felt252, - cost_of_property: u256, - rent_site_only: u256, - rent_one_house: u256, - rent_two_houses: u256, - rent_three_houses: u256, - rent_four_houses: u256, - cost_of_house: u256, - rent_hotel: u256, - is_mortgaged: bool, - group_id: u8, - ); - fn get_property(ref self: T, id: u8, game_id: u256) -> Property; - fn buy_property(ref self: T, property_id: u8, game_id: u256) -> bool; - fn sell_property(ref self: T, property_id: u8, game_id: u256) -> bool; - fn mortgage_property(ref self: T, property_id: u8, game_id: u256) -> bool; - fn unmortgage_property(ref self: T, property_id: u8, game_id: u256) -> bool; - fn collect_rent(ref self: T, property_id: u8, game_id: u256) -> bool; - fn transfer_from( - ref self: T, from: ContractAddress, to: ContractAddress, game_id: u256, amount: u256, - ); - fn buy_house_or_hotel(ref self: T, property_id: u8, game_id: u256) -> bool; - fn sell_house_or_hotel(ref self: T, property_id: u8, game_id: u256) -> bool; - fn mint(ref self: T, recepient: ContractAddress, game_id: u256, amount: u256); - fn get_players_balance(ref self: T, player: ContractAddress, game_id: u256) -> u256; - - fn get_properties_owned_by_player( - ref self: T, player: ContractAddress, game_id: u256, - ) -> Array; - fn get_properties_by_group(ref self: T, group_id: u8, game_id: u256) -> Array; - fn has_monopoly(ref self: T, player: ContractAddress, group_id: u8, game_id: u256) -> bool; - fn collect_rent_with_monopoly(ref self: T, property_id: u8, game_id: u256) -> bool; - fn get_property_value(ref self: T, property_id: u8, game_id: u256) -> u256; - fn can_develop_property(ref self: T, property_id: u8, game_id: u256) -> bool; - fn can_develop_evenly( - ref self: T, property_id: u8, group_id: u8, game_id: u256, is_building: bool, - ) -> bool; - fn can_sell_development(ref self: T, property_id: u8, game_id: u256) -> bool; - fn batch_generate_properties( - ref self: T, - game_id: u256, - properties: Array<(u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8)>, - ); -} diff --git a/src/lib.cairo b/src/lib.cairo index ef6a0e6..a37dae8 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -1,22 +1,29 @@ pub mod systems { - pub mod world; + pub mod actions; + // pub mod Blockopoly; +// pub mod BlockopolyNFT; } pub mod interfaces { - pub mod IWorld; + pub mod IActions; } pub mod model { pub mod game_model; pub mod player_model; pub mod property_model; + pub mod utility_model; + pub mod rail_road_model; + pub mod chance_model; + pub mod community_chest_model; + pub mod jail_model; + pub mod go_free_parking_model; + pub mod tax_model; + pub mod game_player_model; } +pub mod events {} + pub mod tests { mod test_world; - mod test_player_model; - mod test_property_enhanced; } -pub mod utils { - pub mod property_templates; -} diff --git a/src/model/bank_model.cairo b/src/model/bank_model.cairo new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/model/bank_model.cairo @@ -0,0 +1 @@ + diff --git a/src/model/chance_model.cairo b/src/model/chance_model.cairo new file mode 100644 index 0000000..9d8a057 --- /dev/null +++ b/src/model/chance_model.cairo @@ -0,0 +1,46 @@ +#[derive(Copy, Drop, Serde)] +#[dojo::model] +pub struct Chance { + #[key] + pub id: u8, + #[key] + game_id: u256, + name: felt252, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct ChanceToId { + #[key] + pub name: felt252, + pub id: u8, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct IdToChance { + #[key] + pub id: u8, + pub name: felt252, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct ChanceCard { + #[key] + pub id: u8, + #[key] + pub game_id: u256, + pub text: felt252, +} + +pub trait ChanceTrait { + fn new(id: u8, game_id: u256) -> Chance; +} + +impl ChanceImpl of ChanceTrait { + fn new(id: u8, game_id: u256) -> Chance { + Chance { id, game_id, name: 'Chance' } + } +} + diff --git a/src/model/community_chest_model.cairo b/src/model/community_chest_model.cairo new file mode 100644 index 0000000..dc227bf --- /dev/null +++ b/src/model/community_chest_model.cairo @@ -0,0 +1,35 @@ +#[derive(Copy, Drop, Serde)] +#[dojo::model] +pub struct CommunityChest { + #[key] + pub id: u8, + #[key] + game_id: u256, + name: felt252, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct CommunityChestToId { + #[key] + pub name: felt252, + pub id: u8, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct IdToCommunityChest { + #[key] + pub id: u8, + pub name: felt252, +} + +pub trait CommunityChestTrait { + fn new(id: u8, game_id: u256) -> CommunityChest; +} + +impl CommunityChestImpl of CommunityChestTrait { + fn new(id: u8, game_id: u256) -> CommunityChest { + CommunityChest { id, game_id, name: 'Community Chest' } + } +} diff --git a/src/model/game_model.cairo b/src/model/game_model.cairo index bf51a8f..99e1c07 100644 --- a/src/model/game_model.cairo +++ b/src/model/game_model.cairo @@ -1,4 +1,5 @@ use starknet::{ContractAddress, contract_address_const}; +use dojo_starter::model::game_player_model::{GamePlayer, PlayerSymbol, GamePlayerTrait}; // Keeps track of the state of the game #[derive(Serde, Copy, Drop, Introspect, PartialEq)] @@ -6,10 +7,9 @@ use starknet::{ContractAddress, contract_address_const}; pub struct GameCounter { #[key] pub id: felt252, - pub current_val: u64, + pub current_val: u256, } - #[derive(Serde, Copy, Drop, Introspect, PartialEq)] #[dojo::model] pub struct GameBalance { @@ -20,7 +20,8 @@ pub struct GameBalance { pub balance: u256, } -#[derive(Drop, Serde)] + +#[derive(Drop, Clone, Serde)] #[dojo::model] pub struct Game { #[key] @@ -28,10 +29,10 @@ pub struct Game { pub created_by: felt252, // Address of the game creator pub is_initialised: bool, // Indicate whether game with given Id has been created/initialised pub status: GameStatus, // Status of the game - pub mode: GameMode, // Mode 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 next_player: felt252, // Address of the player to make the next move + 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 pub rolls_times: u256, // Total number of times the dice has been rolled @@ -55,6 +56,10 @@ pub struct Game { pub player_battleship: felt252, // item use address on the board pub player_boot: felt252, // item use address on the board pub player_wheelbarrow: felt252, + pub players_joined: u8, + pub game_players: Array, + pub chance: Array, + pub community: Array, } pub trait GameTrait { @@ -62,7 +67,7 @@ pub trait GameTrait { fn new( id: u256, created_by: felt252, - game_mode: GameMode, + game_type: GameType, player_hat: felt252, player_car: felt252, player_dog: felt252, @@ -72,6 +77,9 @@ pub trait GameTrait { player_boot: felt252, player_wheelbarrow: felt252, number_of_players: u8, + game_players: Array, + chance: Array, + community: Array, ) -> Game; fn restart(ref self: Game); fn terminate_game(ref self: Game); @@ -90,9 +98,9 @@ pub enum GameStatus { // Represents the game mode // Can either be SinglePlayer or Multiplayer #[derive(Serde, Copy, Drop, Introspect, PartialEq)] -pub enum GameMode { - SinglePlayer, // Play with computer - MultiPlayer // Play online with friends +pub enum GameType { + PublicGame, // Play with computer + PrivateGame // Play online with friends } @@ -100,7 +108,7 @@ impl GameImpl of GameTrait { fn new( id: u256, created_by: felt252, - game_mode: GameMode, + game_type: GameType, player_hat: felt252, player_car: felt252, player_dog: felt252, @@ -110,6 +118,9 @@ impl GameImpl of GameTrait { player_boot: felt252, player_wheelbarrow: felt252, number_of_players: u8, + game_players: Array, + chance: Array, + community: Array, ) -> Game { let zero_address = contract_address_const::<0x0>(); Game { @@ -117,7 +128,7 @@ impl GameImpl of GameTrait { created_by, is_initialised: true, status: GameStatus::Pending, - mode: game_mode, + mode: game_type, ready_to_start: false, player_hat, player_car, @@ -161,6 +172,10 @@ impl GameImpl of GameTrait { battleship: 'battleship', boot: 'boot', wheelbarrow: 'wheelbarrow', + players_joined: 0, + game_players, + chance, + community, } } @@ -180,3 +195,15 @@ impl GameImpl of GameTrait { } } + +#[generate_trait] +pub impl GameBalanceImpl of IGameBalance { + fn deduct_game_balance(ref self: GameBalance, amount: u256) -> bool { + true + } + + fn increase_game_balance(ref self: GameBalance, amount: u256) -> bool { + true + } +} + diff --git a/src/model/game_player_model.cairo b/src/model/game_player_model.cairo new file mode 100644 index 0000000..f0cc122 --- /dev/null +++ b/src/model/game_player_model.cairo @@ -0,0 +1,125 @@ +use starknet::{ContractAddress}; + +#[derive(Drop, Serde, Clone, Introspect)] +#[dojo::model] +pub struct GamePlayer { + #[key] + pub address: ContractAddress, // links to Player + #[key] + pub game_id: u256, // unique per game + pub player_symbol: PlayerSymbol, + pub is_next: bool, + pub dice_rolled: u8, + pub position: u8, + pub jailed: bool, + pub balance: u256, + pub properties_owned: Array, + pub chance_jail_card: bool, + pub comm_free_card: bool, + pub total_houses_owned: u8, + pub total_hotels_owned: u8, + pub no_of_utilities: u8, + pub no_of_railways: u8, + pub no_section1: u8, + pub no_section2: u8, + pub no_section3: u8, + pub no_section4: u8, + pub no_section5: u8, + pub no_section6: u8, + pub no_section7: u8, + pub no_section8: u8, + pub is_bankrupt: bool, + pub is_active: bool, +} + + +// the GamePlayerTrait tell imposes the actions a player can perform within a game + +pub trait GamePlayerTrait { + fn create_game_player( + username: felt252, address: ContractAddress, game_id: u256, player_symbol: PlayerSymbol, + ) -> GamePlayer; + fn move(player: GamePlayer, steps: u8) -> GamePlayer; + fn pay_game_player(ref self: GamePlayer, amount: u256) -> bool; + fn deduct_game_player(ref self: GamePlayer, amount: u256) -> bool; + fn add_property_to_game_player(ref self: GamePlayer, property_id: u8) -> bool; + fn remove_property_from_game_player(ref self: GamePlayer, property_id: u8) -> bool; + fn declare_bankruptcy(ref self: GamePlayer) -> bool; + fn jail_game_player(ref self: GamePlayer) -> bool; +} + +impl GamePlayerImpl of GamePlayerTrait { + fn create_game_player( + username: felt252, address: ContractAddress, game_id: u256, player_symbol: PlayerSymbol, + ) -> GamePlayer { + GamePlayer { + address, + game_id, + dice_rolled: 0, + player_symbol: player_symbol, + balance: 0, + is_next: true, + position: 0, + jailed: false, + is_bankrupt: false, + is_active: true, + properties_owned: array![], + chance_jail_card: false, + comm_free_card: false, + total_houses_owned: 0, + total_hotels_owned: 0, + no_of_utilities: 0, + no_of_railways: 0, + no_section1: 0, + no_section2: 0, + no_section3: 0, + no_section4: 0, + no_section5: 0, + no_section6: 0, + no_section7: 0, + no_section8: 0, + } + } + + fn move(mut player: GamePlayer, steps: u8) -> GamePlayer { + player.position += steps; + player + } + + fn pay_game_player(ref self: GamePlayer, amount: u256) -> bool { + true + } + + fn deduct_game_player(ref self: GamePlayer, amount: u256) -> bool { + true + } + + fn add_property_to_game_player(ref self: GamePlayer, property_id: u8) -> bool { + true + } + + fn remove_property_from_game_player(ref self: GamePlayer, property_id: u8) -> bool { + true + } + + fn declare_bankruptcy(ref self: GamePlayer) -> bool { + true + } + + fn jail_game_player(ref self: GamePlayer) -> bool { + true + } +} + +#[derive(Serde, Copy, Introspect, Drop, PartialEq)] +pub enum PlayerSymbol { + Hat, + Car, + Dog, + Thimble, + Iron, + Battleship, + Boot, + Wheelbarrow, +} + diff --git a/src/model/go_free_parking_model.cairo b/src/model/go_free_parking_model.cairo new file mode 100644 index 0000000..543c912 --- /dev/null +++ b/src/model/go_free_parking_model.cairo @@ -0,0 +1,26 @@ +#[derive(Copy, Drop, Serde)] +#[dojo::model] +pub struct Go { + #[key] + pub id: u8, + #[key] + pub game_id: u256, + pub name: felt252, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct GoToId { + #[key] + pub name: felt252, + pub id: u8, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct IdToGo { + #[key] + pub id: u8, + pub name: felt252, +} + diff --git a/src/model/jail_model.cairo b/src/model/jail_model.cairo new file mode 100644 index 0000000..3de5413 --- /dev/null +++ b/src/model/jail_model.cairo @@ -0,0 +1,36 @@ +#[derive(Copy, Drop, Serde)] +#[dojo::model] +pub struct Jail { + #[key] + pub id: u8, + #[key] + pub game_id: u256, + pub name: felt252, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct JailToId { + #[key] + pub name: felt252, + pub id: u8, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct IdToJail { + #[key] + pub id: u8, + pub name: felt252, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct JailCard { + #[key] + pub id: u8, + #[key] + pub game_id: u256, + pub text: felt252, +} + diff --git a/src/model/player_model.cairo b/src/model/player_model.cairo index e4c0613..9ed5cf5 100644 --- a/src/model/player_model.cairo +++ b/src/model/player_model.cairo @@ -1,346 +1,43 @@ use starknet::{ContractAddress, contract_address_const}; -pub fn zero_address() -> ContractAddress { - contract_address_const::<0>() -} -// #[derive(Copy, Drop, Serde, Debug)] #[derive(Drop, Copy, Serde)] #[dojo::model] pub struct Player { #[key] - pub player: ContractAddress, + pub address: ContractAddress, pub username: felt252, - pub is_bot: bool, - pub new_owner: ContractAddress, + pub is_registered: bool, pub balance: u256, - pub position: u8, - pub jailed: bool, - pub player_symbol: PlayerSymbol, pub total_games_played: u256, pub total_games_completed: u256, pub total_games_won: u256, + pub created_at: u64, + pub updated_at: u64, } pub trait PlayerTrait { - // Create a new player - // `username` - Username to assign to the new player - // `owner` - Account owner of player - // returns the created player - fn new( - username: felt252, - player: ContractAddress, - is_bot: bool, - player_symbol: PlayerSymbol, - initial_balance: u256, - ) -> Player; - fn move_player(ref self: Player, steps: u8, board_size: u8); - fn pay_rent_to(ref self: Player, ref recipient: Player, amount: u256) -> bool; - fn buy_property(ref self: Player, ref seller: Player, amount: u256) -> bool; - fn update_stats(ref self: Player, played: u256, completed: u256, won: u256); - fn set_jail_status(ref self: Player, jailed: bool); - fn transfer_ownership(ref self: Player, new_owner: ContractAddress); - fn collect_go_money(ref self: Player, amount: u256); - fn pay_fee(ref self: Player, amount: u256) -> bool; - fn can_afford(self: @Player, amount: u256) -> bool; - fn is_bankrupt(self: @Player) -> bool; - fn get_win_rate(self: @Player) -> u256; + fn new(username: felt252, address: ContractAddress, created_at: u64) -> Player; } -pub impl PlayerImpl of PlayerTrait { - fn new( - username: felt252, - player: ContractAddress, - is_bot: bool, - player_symbol: PlayerSymbol, - initial_balance: u256, - ) -> Player { +impl PlayerImpl of PlayerTrait { + fn new(username: felt252, address: ContractAddress, created_at: u64) -> Player { + // let zero_address: ContractAddress = contract_address_const::<0>(); Player { - player, + address, username, - is_bot, - new_owner: zero_address(), - balance: initial_balance, - position: 0, - jailed: false, - player_symbol, + is_registered: true, + created_at, + updated_at: created_at, + balance: 0, total_games_played: 0, total_games_completed: 0, total_games_won: 0, } } - - fn move_player(ref self: Player, steps: u8, board_size: u8) { - assert(board_size > 0, 'Board not greater than 0'); - - let old_position = self.position; - let total_steps = old_position + steps; - let new_position = total_steps % board_size; - - let laps = total_steps / board_size; - self.balance += 200_u256 * laps.into(); - - self.position = new_position; - } - fn pay_rent_to(ref self: Player, ref recipient: Player, amount: u256) -> bool { - if !self.can_afford(amount) { - return false; - } - - if self.player == recipient.player { - return false; - } - - if amount == 0 { - return false; - } - - self.balance -= amount; - recipient.balance += amount; - - true - } - - fn buy_property(ref self: Player, ref seller: Player, amount: u256) -> bool { - if !self.can_afford(amount) { - return false; - } - - if self.player == seller.player { - return false; - } - - if amount == 0 { - return false; - } - - self.balance -= amount; - seller.balance += amount; - - true - } - - fn update_stats(ref self: Player, played: u256, completed: u256, won: u256) { - self.total_games_played += played; - self.total_games_completed += completed; - self.total_games_won += won; - } - - fn set_jail_status(ref self: Player, jailed: bool) { - self.jailed = jailed; - if jailed { - self.position = 10; - } - } - - fn transfer_ownership(ref self: Player, new_owner: ContractAddress) { - self.new_owner = new_owner; - } - - fn collect_go_money(ref self: Player, amount: u256) { - self.balance += amount; - } - - fn pay_fee(ref self: Player, amount: u256) -> bool { - if !self.can_afford(amount) { - return false; - } - - self.balance -= amount; - true - } - - fn can_afford(self: @Player, amount: u256) -> bool { - *self.balance >= amount - } - - fn is_bankrupt(self: @Player) -> bool { - *self.balance == 0 - } - - fn get_win_rate(self: @Player) -> u256 { - if *self.total_games_completed == 0 { - return 0; - } - (*self.total_games_won * 100) / *self.total_games_completed - } -} - -pub trait PlayerSymbolTrait { - fn is_valid(symbol: PlayerSymbol) -> bool; - fn get_name(symbol: PlayerSymbol) -> felt252; - fn from_felt(value: felt252) -> Option; -} - -pub impl PlayerSymbolImpl of PlayerSymbolTrait { - fn is_valid(symbol: PlayerSymbol) -> bool { - match symbol { - PlayerSymbol::Hat => true, - PlayerSymbol::Car => true, - PlayerSymbol::Dog => true, - PlayerSymbol::Thimble => true, - PlayerSymbol::Iron => true, - PlayerSymbol::Battleship => true, - PlayerSymbol::Boot => true, - PlayerSymbol::Wheelbarrow => true, - } - } - - fn get_name(symbol: PlayerSymbol) -> felt252 { - match symbol { - PlayerSymbol::Hat => 'Hat', - PlayerSymbol::Car => 'Car', - PlayerSymbol::Dog => 'Dog', - PlayerSymbol::Thimble => 'Thimble', - PlayerSymbol::Iron => 'Iron', - PlayerSymbol::Battleship => 'Battleship', - PlayerSymbol::Boot => 'Boot', - PlayerSymbol::Wheelbarrow => 'Wheelbarrow', - } - } - - fn from_felt(value: felt252) -> Option { - if value == 'Hat' { - Option::Some(PlayerSymbol::Hat) - } else if value == 'Car' { - Option::Some(PlayerSymbol::Car) - } else if value == 'Dog' { - Option::Some(PlayerSymbol::Dog) - } else if value == 'Thimble' { - Option::Some(PlayerSymbol::Thimble) - } else if value == 'Iron' { - Option::Some(PlayerSymbol::Iron) - } else if value == 'Battleship' { - Option::Some(PlayerSymbol::Battleship) - } else if value == 'Boot' { - Option::Some(PlayerSymbol::Boot) - } else if value == 'Wheelbarrow' { - Option::Some(PlayerSymbol::Wheelbarrow) - } else { - Option::None - } - } -} - - -pub trait PlayerValidation { - fn validate_creation( - username: felt252, - player: ContractAddress, - player_symbol: PlayerSymbol, - initial_balance: u256, - ) -> bool; - - fn validate_transaction(payer: @Player, recipient: @Player, amount: u256) -> bool; - - fn validate_username(username: felt252) -> bool; } -pub impl PlayerValidationImpl of PlayerValidation { - fn validate_creation( - username: felt252, - player: ContractAddress, - player_symbol: PlayerSymbol, - initial_balance: u256, - ) -> bool { - if player == zero_address() { - return false; - } - - if username == 0 { - return false; - } - - if !PlayerSymbolImpl::is_valid(player_symbol) { - return false; - } - - true - } - - fn validate_transaction(payer: @Player, recipient: @Player, amount: u256) -> bool { - if !payer.can_afford(amount) { - return false; - } - - if *payer.player == *recipient.player { - return false; - } - - if amount == 0 { - return false; - } - - true - } - - fn validate_username(username: felt252) -> bool { - username != 0 - } -} - -pub trait PlayerActions { - fn go_to_jail(ref self: Player); - fn get_out_of_jail(ref self: Player, pay_fee: bool, fee_amount: u256) -> bool; - fn pass_go(ref self: Player, go_amount: u256); - fn pay_tax(ref self: Player, tax_amount: u256) -> bool; - fn receive_money(ref self: Player, amount: u256); - fn reset_position(ref self: Player); -} - -pub impl PlayerActionsImpl of PlayerActions { - fn go_to_jail(ref self: Player) { - self.jailed = true; - self.position = 10; - } - - fn get_out_of_jail(ref self: Player, pay_fee: bool, fee_amount: u256) -> bool { - if pay_fee { - if !self.can_afford(fee_amount) { - return false; - } - self.balance -= fee_amount; - } - - self.jailed = false; - true - } - - fn pass_go(ref self: Player, go_amount: u256) { - self.balance += go_amount; - } - - fn pay_tax(ref self: Player, tax_amount: u256) -> bool { - if !self.can_afford(tax_amount) { - return false; - } - - self.balance -= tax_amount; - true - } - - fn receive_money(ref self: Player, amount: u256) { - self.balance += amount; - } - - fn reset_position(ref self: Player) { - self.position = 0; - self.jailed = false; - } -} - -#[derive(Serde, Copy, Drop, Introspect, PartialEq)] -pub enum PlayerSymbol { - Hat, - Car, - Dog, - Thimble, - Iron, - Battleship, - Boot, - Wheelbarrow, -} #[derive(Drop, Copy, Serde)] #[dojo::model] @@ -357,3 +54,11 @@ pub struct AddressToUsername { pub address: ContractAddress, pub username: felt252, } + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct IsRegistered { + #[key] + pub address: ContractAddress, + pub is_registered: bool, +} diff --git a/src/model/property_model.cairo b/src/model/property_model.cairo index 2b404cb..44bd328 100644 --- a/src/model/property_model.cairo +++ b/src/model/property_model.cairo @@ -6,10 +6,12 @@ pub struct Property { #[key] pub id: u8, #[key] - game_id: u256, + pub game_id: u256, pub name: felt252, pub owner: ContractAddress, + pub property_type: PropertyType, pub cost_of_property: u256, + pub property_level: u8, pub rent_site_only: u256, pub rent_one_house: u256, pub rent_two_houses: u256, @@ -22,6 +24,20 @@ pub struct Property { pub for_sale: bool, pub development: u8, } +#[derive(Serde, Copy, Drop, Introspect, PartialEq, Debug)] +pub enum PropertyType { + Go, + Chance, + CommunityChest, + Jail, + Utility, + RailRoad, + Tax, + FreeParking, + Property, + VisitingJail, +} + #[derive(Drop, Copy, Serde)] #[dojo::model] @@ -45,6 +61,7 @@ pub trait PropertyTrait { game_id: u256, name: felt252, cost: u256, + property_type: PropertyType, rent_site_only: u256, rent_one_house: u256, rent_two_houses: u256, @@ -53,19 +70,30 @@ pub trait PropertyTrait { cost_of_house: u256, rent_hotel: u256, group_id: u8, + owner: ContractAddress, ) -> Property; - fn set_owner(property: Property, new_owner: ContractAddress); - fn get_rent_amount(property: Property, houses: u8, hotel: bool) -> u256; - fn mortgage(property: Property); - fn lift_mortgage(property: Property); + fn get_rent_amount( + self: @Property, owner_railroads: u8, owner_utilities: u8, dice_rolled: u256, + ) -> u256; + fn calculate_utility_rent(self: @Property, no_of_utilities: u8, dice_rolled: u256) -> u256; + fn calculate_railway_rent(self: @Property, no_of_railways: u8) -> u256; + fn mortgage(ref self: Property, owner: ContractAddress); + fn lift_mortgage(ref self: Property, owner: ContractAddress); + fn upgrade_property(ref self: Property, player: ContractAddress, upgrade_level: u8) -> bool; + fn downgrade_property(ref self: Property, player: ContractAddress, downgrade_level: u8) -> bool; + fn change_game_property_ownership( + ref self: Property, new_owner: ContractAddress, owner: ContractAddress, + ) -> bool; } + impl PropertyImpl of PropertyTrait { fn new( id: u8, game_id: u256, name: felt252, cost: u256, + property_type: PropertyType, rent_site_only: u256, rent_one_house: u256, rent_two_houses: u256, @@ -74,14 +102,16 @@ impl PropertyImpl of PropertyTrait { cost_of_house: u256, rent_hotel: u256, group_id: u8, + owner: ContractAddress, ) -> Property { - let zero_address: ContractAddress = contract_address_const::<0>(); Property { id, game_id, name, - owner: zero_address, + owner: owner, + property_type, cost_of_property: cost, + property_level: 0, rent_site_only: rent_site_only, rent_one_house: rent_one_house, rent_two_houses: rent_two_houses, @@ -95,34 +125,92 @@ impl PropertyImpl of PropertyTrait { development: 0, } } + fn get_rent_amount( + self: @Property, owner_railroads: u8, owner_utilities: u8, dice_rolled: u256, + ) -> u256 { + if *self.is_mortgaged { + return 0; + } - - fn set_owner(mut property: Property, new_owner: ContractAddress) { - property.owner = new_owner; + match *self.property_type { + PropertyType::Property => { + match *self.development { + 0 => *self.rent_site_only, + 1 => *self.rent_one_house, + 2 => *self.rent_two_houses, + 3 => *self.rent_three_houses, + 4 => *self.rent_four_houses, + 5 => *self.rent_hotel, + _ => *self.rent_site_only, + } + }, + PropertyType::RailRoad => { + match owner_railroads { + 0 => 0, + 1 => 25, + 2 => 50, + 3 => 100, + 4 => 200, + _ => 0, + } + }, + PropertyType::Utility => { + match owner_utilities { + 0 => 0, + 1 => 4 * dice_rolled, + 2 => 10 * dice_rolled, + _ => 0, + } + }, + _ => 0, + } } - fn get_rent_amount(mut property: Property, houses: u8, hotel: bool) -> u256 { - if property.is_mortgaged { - return 0; - } - if hotel { - return property.rent_hotel; + + fn calculate_utility_rent(self: @Property, no_of_utilities: u8, dice_rolled: u256) -> u256 { + match no_of_utilities { + 0 => 0, + 1 => 4 * dice_rolled, + 2 => 10 * dice_rolled, + _ => 0, } - match houses { - 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_site_only // default fallback + } + fn calculate_railway_rent(self: @Property, no_of_railways: u8) -> u256 { + match no_of_railways { + 0 => 0, + 1 => 25, + 2 => 50, + 3 => 100, + 4 => 200, + _ => 0, } } - fn mortgage(mut property: Property) { - property.is_mortgaged = true; + + fn mortgage(ref self: Property, owner: ContractAddress) { + self.is_mortgaged = true; + } + + fn lift_mortgage(ref self: Property, owner: ContractAddress) { + self.is_mortgaged = false; + } + + fn upgrade_property(ref self: Property, player: ContractAddress, upgrade_level: u8) -> bool { + // deals with the property level mostly after many checks + true + } + + fn downgrade_property( + ref self: Property, player: ContractAddress, downgrade_level: u8, + ) -> bool { + // deals with the property level mostly after many checks + true } - fn lift_mortgage(mut property: Property) { - property.is_mortgaged = false; + fn change_game_property_ownership( + ref self: Property, new_owner: ContractAddress, owner: ContractAddress, + ) -> bool { + // deals with the field owner after many checks + true } } diff --git a/src/model/rail_road_model.cairo b/src/model/rail_road_model.cairo new file mode 100644 index 0000000..0ba5458 --- /dev/null +++ b/src/model/rail_road_model.cairo @@ -0,0 +1,99 @@ +use starknet::{ContractAddress, contract_address_const}; +#[derive(Copy, Drop, Serde)] +#[dojo::model] +pub struct RailRoad { + #[key] + pub id: u8, + #[key] + game_id: u256, + pub name: felt252, + pub owner: ContractAddress, + pub cost_of_railroad: u256, + pub is_mortgaged: bool, + pub for_sale: bool, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct RailRoadToId { + #[key] + pub name: felt252, + pub id: u8, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct IdToRailRoad { + #[key] + pub id: u8, + pub name: felt252, +} + +pub trait RailRoadTrait { + fn new(id: u8, game_id: u256, name: felt252) -> RailRoad; + fn change_railroad_ownership( + railroad: RailRoad, new_owner: ContractAddress, owner: ContractAddress, + ) -> bool; + fn get_rent_amount(railroad: RailRoad, railroad_owned: u8) -> u256; + fn mortgage(railroad: RailRoad, owner: ContractAddress); + fn lift_mortgage(railroad: RailRoad, owner: ContractAddress); +} + +impl RailRoadImpl of RailRoadTrait { + fn new(id: u8, game_id: u256, name: felt252) -> RailRoad { + let zero_address: ContractAddress = contract_address_const::<0>(); + RailRoad { + id, + game_id, + name, + owner: zero_address, + cost_of_railroad: 200, + is_mortgaged: false, + for_sale: true, + } + } + + + // change to ref + fn change_railroad_ownership( + railroad: RailRoad, new_owner: ContractAddress, owner: ContractAddress, + ) -> bool { + // after checks + + true + } + + // change to non changing state + fn get_rent_amount(mut railroad: RailRoad, railroad_owned: u8) -> u256 { + let mut rent = 0; + if railroad.is_mortgaged { + return rent; + } + if railroad_owned == 1 { + rent = 25; + } + if railroad_owned == 2 { + rent = 50; + } + if railroad_owned == 3 { + rent = 100; + } + if railroad_owned == 4 { + rent = 200; + } else { + rent = 0; + } + + rent + } + + + // change to ref + fn mortgage(mut railroad: RailRoad, owner: ContractAddress) { // railroad.is_mortgaged = true; + } + + fn lift_mortgage( + mut railroad: RailRoad, owner: ContractAddress, + ) { // railroad.is_mortgaged = false; + } +} diff --git a/src/model/tax_model.cairo b/src/model/tax_model.cairo new file mode 100644 index 0000000..af8a7e4 --- /dev/null +++ b/src/model/tax_model.cairo @@ -0,0 +1,27 @@ +#[derive(Copy, Drop, Serde)] +#[dojo::model] +pub struct Tax { + #[key] + pub id: u8, + #[key] + pub game_id: u256, + pub name: felt252, + pub tax_amount: u256, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct TaxToId { + #[key] + pub name: felt252, + pub id: u8, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct IdToTax { + #[key] + pub id: u8, + pub name: felt252, +} + diff --git a/src/model/utility_model.cairo b/src/model/utility_model.cairo new file mode 100644 index 0000000..2accc46 --- /dev/null +++ b/src/model/utility_model.cairo @@ -0,0 +1,95 @@ +use starknet::{ContractAddress, contract_address_const}; +#[derive(Copy, Drop, Serde)] +#[dojo::model] +pub struct Utility { + #[key] + pub id: u8, + #[key] + game_id: u256, + pub name: felt252, + pub owner: ContractAddress, + pub cost_of_utility: u256, + pub is_mortgaged: bool, + pub for_sale: bool, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct UtilityToId { + #[key] + pub name: felt252, + pub id: u8, +} + +#[derive(Drop, Copy, Serde)] +#[dojo::model] +pub struct IdToUtility { + #[key] + pub id: u8, + pub name: felt252, +} + +pub trait UtilityTrait { + fn new(id: u8, game_id: u256, name: felt252) -> Utility; + fn change_utility_ownership( + utility: Utility, new_owner: ContractAddress, owner: ContractAddress, + ); + fn get_rent_amount(utility: Utility, utilities_owned: u8, dice_rolled: u8) -> u256; + fn mortgage_utility(utility: Utility, owner: ContractAddress) -> bool; + fn lift_utility_mortgage(utility: Utility, owner: ContractAddress) -> bool; + fn up_utility_up_for_sale(ref self: Utility, owner: ContractAddress) -> bool; +} + +impl UtilityImpl of UtilityTrait { + fn new(id: u8, game_id: u256, name: felt252) -> Utility { + let zero_address: ContractAddress = contract_address_const::<0>(); + Utility { + id, + game_id, + name, + owner: zero_address, + cost_of_utility: 150, + is_mortgaged: false, + for_sale: true, + } + } + + + // use ref state + fn change_utility_ownership( + mut utility: Utility, new_owner: ContractAddress, owner: ContractAddress, + ) { //implement after checks especially ownership ... + } + + // change to no change state + fn get_rent_amount(mut utility: Utility, utilities_owned: u8, dice_rolled: u8) -> u256 { + let mut rent = 0; + if utility.is_mortgaged { + return rent; + } + if utilities_owned == 1 { + rent = 4 * dice_rolled.into(); + } + if utilities_owned == 2 { + rent = 10 * dice_rolled.into(); + } + + rent + } + + //change to ref state changing + fn mortgage_utility(mut utility: Utility, owner: ContractAddress) -> bool { + true + } + + //change to ref state changing + fn lift_utility_mortgage(mut utility: Utility, owner: ContractAddress) -> bool { + true + } + + // + fn up_utility_up_for_sale(ref self: Utility, owner: ContractAddress) -> bool { + // implement after checks ... to chage the state for_sale + true + } +} diff --git a/src/systems/Blockopoly.cairo b/src/systems/Blockopoly.cairo new file mode 100644 index 0000000..1c1c631 --- /dev/null +++ b/src/systems/Blockopoly.cairo @@ -0,0 +1,90 @@ +// // SPDX-License-Identifier: MIT +// use starknet::ContractAddress; +// use dojo_starter::interfaces::IBlockopoly::IBlockopoly; + +// #[dojo::contract] +// pub mod Blockopoly { +// use openzeppelin::access::ownable::OwnableComponent; +// use openzeppelin::token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; +// use starknet::ContractAddress; +// use super::IBlockopoly; + +// component!(path: ERC20Component, storage: erc20, event: ERC20Event); +// component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + +// #[abi(embed_v0)] +// impl ERC20Impl = ERC20Component::ERC20Impl; +// #[abi(embed_v0)] +// impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; +// impl InternalImpl = ERC20Component::InternalImpl; + +// #[abi(embed_v0)] +// impl OwnableImpl = OwnableComponent::OwnableImpl; +// impl OwnableInternalImpl = OwnableComponent::InternalImpl; + +// #[storage] +// pub struct Storage { +// #[substorage(v0)] +// pub erc20: ERC20Component::Storage, +// #[substorage(v0)] +// pub ownable: OwnableComponent::Storage, +// } + +// #[event] +// #[derive(Drop, starknet::Event)] +// enum Event { +// #[flat] +// ERC20Event: ERC20Component::Event, +// #[flat] +// OwnableEvent: OwnableComponent::Event, +// } + +// #[abi(embed_v0)] +// impl BlockopolyImpl of IBlockopoly { +// fn init(ref self: ContractState, owner: ContractAddress) { +// self.erc20.initializer(format!("USDC"), format!("USDC")); +// self.ownable.initializer(owner); + +// self.erc20.mint(owner, 1000000_u256); +// } +// fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { +// self.erc20.mint(recipient, amount); +// } + +// fn get_balance(ref self: ContractState, address: ContractAddress) -> u256 { +// let balance = self.erc20.balance_of(address); +// balance +// } + +// fn transferFrom( +// ref self: ContractState, +// sender: ContractAddress, +// recipient: ContractAddress, +// amount: u256, +// ) -> bool { +// let success = self.erc20.transfer_from(sender, recipient, amount); +// success +// } + +// fn approve_user(ref self: ContractState, spender: ContractAddress, amount: u256) { +// self.erc20.approve(spender, amount); +// } + +// fn get_allowance( +// ref self: ContractState, owner: ContractAddress, spender: ContractAddress, +// ) -> u256 { +// let allowed_amount = self.erc20.allowance(owner, spender); +// allowed_amount +// } + +// fn get_name(ref self: ContractState) -> ByteArray { +// let name = self.erc20.name(); +// name +// } + +// fn get_symbol(ref self: ContractState) -> ByteArray { +// let symbol = self.erc20.symbol(); +// symbol +// } +// } +// } diff --git a/src/systems/BlockopolyNFT.cairo b/src/systems/BlockopolyNFT.cairo new file mode 100644 index 0000000..5420a7e --- /dev/null +++ b/src/systems/BlockopolyNFT.cairo @@ -0,0 +1,49 @@ +// use starknet::ContractAddress; +// use dojo_starter::interfaces::IBlockopolyNFT::IBlockopolyNFT; + +// #[starknet::contract] +// pub mod BlockopolyNFT { +// use starknet::ContractAddress; +// use openzeppelin::introspection::src5::SRC5Component; +// use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; +// use super::IBlockopolyNFT; + +// component!(path: ERC721Component, storage: erc721, event: ERC721Event); +// component!(path: SRC5Component, storage: src5, event: SRC5Event); + +// #[storage] +// struct Storage { +// #[substorage(v0)] +// erc721: ERC721Component::Storage, +// #[substorage(v0)] +// src5: SRC5Component::Storage, +// } + +// #[event] +// #[derive(Drop, starknet::Event)] +// enum Event { +// #[flat] +// ERC721Event: ERC721Component::Event, +// #[flat] +// SRC5Event: SRC5Component::Event, +// } + +// #[constructor] +// fn constructor( +// ref self: ContractState, base_uri: ByteArray, name_: ByteArray, symbol: ByteArray, +// ) { +// self.erc721.initializer(name_, symbol, base_uri); +// } + +// #[abi(embed_v0)] +// impl ERC721Impl = ERC721Component::ERC721MixinImpl; + +// impl ERC721InternalImpl = ERC721Component::InternalImpl; + +// #[abi(embed_v0)] +// impl BlockopolyNFTImpl of IBlockopolyNFT { +// fn mint(ref self: ContractState, recipient: ContractAddress, token_id: u256) { +// self.erc721.mint(recipient, token_id); +// } +// } +// } diff --git a/src/systems/actions.cairo b/src/systems/actions.cairo new file mode 100644 index 0000000..45f3a59 --- /dev/null +++ b/src/systems/actions.cairo @@ -0,0 +1,2157 @@ +// dojo decorator +#[dojo::contract] +pub mod actions { + use dojo_starter::interfaces::IActions::IActions; + use dojo_starter::model::property_model::{ + Property, PropertyType, PropertyTrait, PropertyToId, IdToProperty, + }; + use dojo_starter::model::utility_model::{Utility, UtilityTrait, UtilityToId, IdToUtility}; + use dojo_starter::model::rail_road_model::{RailRoad, RailRoadTrait, RailRoadToId, IdToRailRoad}; + use dojo_starter::model::game_model::{ + GameType, Game, GameBalance, GameTrait, GameCounter, GameStatus, IGameBalance, + }; + use dojo_starter::model::player_model::{ + Player, UsernameToAddress, AddressToUsername, PlayerTrait, IsRegistered, + }; + use dojo_starter::model::game_player_model::{GamePlayer, PlayerSymbol, GamePlayerTrait}; + use dojo_starter::model::chance_model::{Chance, ChanceTrait}; + use dojo_starter::model::community_chest_model::{CommunityChest, CommunityChestTrait}; + use dojo_starter::model::jail_model::{Jail}; + use dojo_starter::model::go_free_parking_model::{Go}; + use dojo_starter::model::tax_model::{Tax}; + use starknet::{ + ContractAddress, get_caller_address, get_block_timestamp, contract_address_const, + get_contract_address, + }; + + use dojo::model::{ModelStorage}; + use dojo::event::EventStorage; + use origami_random::dice::{Dice, DiceTrait}; + + + #[derive(Copy, Drop, Serde)] + #[dojo::event] + pub struct GameCreated { + #[key] + pub game_id: u256, + pub timestamp: u64, + } + + #[derive(Copy, Drop, Serde)] + #[dojo::event] + pub struct PlayerCreated { + #[key] + pub username: felt252, + #[key] + pub player: ContractAddress, + pub timestamp: u64, + } + + #[derive(Copy, Drop, Serde)] + #[dojo::event] + pub struct GameStarted { + #[key] + pub game_id: u256, + pub timestamp: u64, + } + + #[derive(Copy, Drop, Serde)] + #[dojo::event] + pub struct PlayerJoined { + #[key] + pub game_id: u256, + #[key] + pub username: felt252, + pub timestamp: u64, + } + + + #[abi(embed_v0)] + impl ActionsImpl of IActions { + fn is_registered(self: @ContractState, address: ContractAddress) -> bool { + let world = self.world_default(); + let is_registered: IsRegistered = world.read_model(address); + is_registered.is_registered + } + + fn roll_dice(ref self: ContractState) -> (u8, u8) { + let seed = get_block_timestamp(); + + let mut dice1 = DiceTrait::new(6, seed.try_into().unwrap()); + let mut dice2 = DiceTrait::new(6, (seed + 1).try_into().unwrap()); + + let dice1_roll = dice1.roll(); + let dice2_roll = dice2.roll(); + + (dice1_roll, dice2_roll) + } + + fn get_username_from_address(self: @ContractState, address: ContractAddress) -> felt252 { + let world = self.world_default(); + + let address_map: AddressToUsername = world.read_model(address); + + address_map.username + } + + fn register_new_player(ref self: ContractState, username: felt252) { + let mut world = self.world_default(); + + let caller: ContractAddress = get_caller_address(); + + let zero_address: ContractAddress = contract_address_const::<0x0>(); + + let timestamp = get_block_timestamp(); + + // Validate username + assert(username != 0, 'USERNAME CANNOT BE ZERO'); + + // Check if the player already exists (ensure username is unique) + let existing_player: UsernameToAddress = world.read_model(username); + assert(existing_player.address == zero_address, 'USERNAME ALREADY TAKEN'); + + // Ensure player cannot update username by calling this function + let existing_username = self.get_username_from_address(caller); + + assert(existing_username == 0, 'USERNAME ALREADY CREATED'); + + let new_player: Player = PlayerTrait::new(username, caller, timestamp); + let username_to_address: UsernameToAddress = UsernameToAddress { + username, address: caller, + }; + let address_to_username: AddressToUsername = AddressToUsername { + address: caller, username, + }; + let mut is_registered: IsRegistered = world.read_model(caller); + is_registered.is_registered = true; + + world.write_model(@is_registered); + world.write_model(@new_player); + world.write_model(@username_to_address); + world.write_model(@address_to_username); + world + .emit_event( + @PlayerCreated { username, player: caller, timestamp: get_block_timestamp() }, + ); + } + + fn get_tax(self: @ContractState, id: u8, game_id: u256) -> Tax { + let world = self.world_default(); + let tax: Tax = world.read_model((id, game_id)); + tax + } + + fn get_go(self: @ContractState, id: u8, game_id: u256) -> Go { + let world = self.world_default(); + let go: Go = world.read_model((id, game_id)); + go + } + + fn get_chance(self: @ContractState, id: u8, game_id: u256) -> Chance { + let world = self.world_default(); + let chance: Chance = world.read_model((id, game_id)); + chance + } + + fn get_community_chest(self: @ContractState, id: u8, game_id: u256) -> CommunityChest { + let world = self.world_default(); + let community_chest: CommunityChest = world.read_model((id, game_id)); + community_chest + } + + fn get_property(self: @ContractState, id: u8, game_id: u256) -> Property { + let world = self.world_default(); + let property: Property = world.read_model((id, game_id)); + property + } + + fn get_utility(self: @ContractState, id: u8, game_id: u256) -> Utility { + let world = self.world_default(); + let utility: Utility = world.read_model((id, game_id)); + utility + } + + fn get_railroad(self: @ContractState, id: u8, game_id: u256) -> RailRoad { + let world = self.world_default(); + let railroad: RailRoad = world.read_model((id, game_id)); + railroad + } + + fn get_jail(self: @ContractState, id: u8, game_id: u256) -> Jail { + let world = self.world_default(); + let jail: Jail = world.read_model((id, game_id)); + jail + } + + fn start_game(ref self: ContractState, game_id: u256) -> bool { + let mut world = self.world_default(); + let mut game: Game = world.read_model(game_id); + + game.status = GameStatus::Ongoing; + game.next_player = get_caller_address(); + + let len = game.game_players.len(); + let mut i = 0; + while i < len { + self.mint(*game.game_players[i], 1, 1500); + i += 1; + }; + world.write_model(@game); + true + } + + // to stay and call models + fn create_new_game( + ref self: ContractState, + game_type: GameType, + player_symbol: PlayerSymbol, + number_of_players: u8, + ) -> u256 { + // Get default world + let mut world = self.world_default(); + + assert(number_of_players >= 2 && number_of_players <= 8, 'invalid no of players'); + + // Get the account address of the caller + let caller_address = get_caller_address(); + + let caller_username = self.get_username_from_address(caller_address); + assert(caller_username != 0, 'PLAYER NOT REGISTERED'); + + let game_id = self.create_new_game_id(); + let mut player: GamePlayer = world.read_model((caller_address, game_id)); + let timestamp = get_block_timestamp(); + + // Initialize player symbols + let ( + player_hat, + player_car, + player_dog, + player_thimble, + player_iron, + player_battleship, + player_boot, + player_wheelbarrow, + ) = + match player_symbol { + PlayerSymbol::Hat => (caller_username, 0, 0, 0, 0, 0, 0, 0), + PlayerSymbol::Car => (0, caller_username, 0, 0, 0, 0, 0, 0), + PlayerSymbol::Dog => (0, 0, caller_username, 0, 0, 0, 0, 0), + PlayerSymbol::Thimble => (0, 0, 0, caller_username, 0, 0, 0, 0), + PlayerSymbol::Iron => (0, 0, 0, 0, caller_username, 0, 0, 0), + PlayerSymbol::Battleship => (0, 0, 0, 0, 0, caller_username, 0, 0), + PlayerSymbol::Boot => (0, 0, 0, 0, 0, 0, caller_username, 0), + PlayerSymbol::Wheelbarrow => (0, 0, 0, 0, 0, 0, 0, caller_username), + }; + + let mut game_player = ArrayTrait::new(); + game_player.append(caller_address); + + let chance = self.generate_chance_deck(); + let community = self.generate_community_chest_deck(); + + // Create a new game + let mut new_game: Game = GameTrait::new( + game_id, + caller_username, + game_type, + player_hat, + player_car, + player_dog, + player_thimble, + player_iron, + player_battleship, + player_boot, + player_wheelbarrow, + number_of_players, + game_player, + chance, + community, + ); + // Generate tiles + self.generate_board_tiles(game_id); + // Set visibility based on game mode + let mut emitted_game_id = game_id; + + if (game_type == GameType::PrivateGame) { + emitted_game_id = 0; + } + + new_game.players_joined += 1; + + // Save game to storage + world.write_model(@new_game); + + world.emit_event(@GameCreated { game_id: emitted_game_id, timestamp }); + + game_id + } + + /// Allows a registered player to join a pending game by selecting a symbol. + /// Automatically starts the game once the required number of players have joined. + fn join_game(ref self: ContractState, player_symbol: PlayerSymbol, game_id: u256) { + // Load world state + let mut world = self.world_default(); + + // Retrieve game from storage + let mut game: Game = world.read_model(game_id); + + // Ensure the game has been initialized + assert(game.is_initialised, 'GAME NOT INITIALISED'); + + // Ensure the game still has room for new players + assert(game.players_joined < game.number_of_players, 'ROOM FILLED'); + + // Ensure the game is in the Pending state + assert(game.status == GameStatus::Pending, 'GAME NOT PENDING'); + + // Get the caller's address and corresponding username + let caller_address = get_caller_address(); + let caller_username = self.get_username_from_address(caller_address); + + // Ensure the caller is a registered player + assert(caller_username != 0, 'PLAYER NOT REGISTERED'); + + // Ensure the player hasn't already joined under a different symbol + self.assert_player_not_already_joined(game.clone(), caller_username); + + // Attempt to join the game with the selected symbol + self.try_join_symbol(game.clone(), player_symbol, caller_username, game_id); + + // Emit event for player joining + world + .emit_event( + @PlayerJoined { + game_id, username: caller_username, timestamp: get_block_timestamp(), + }, + ); + + // Recount players and update the joined count + game.players_joined = self.count_joined_players(game.clone()); + game.game_players.append(get_caller_address()); + + // Start the game if all players have joined + if game.players_joined == game.number_of_players { + game.status = GameStatus::Ongoing; + world.emit_event(@GameStarted { game_id, timestamp: get_block_timestamp() }); + } + + // Persist the updated game state + world.write_model(@game); + } + + + fn sell_property(ref self: ContractState, property_id: u8, game_id: u256) -> bool { + let mut world = self.world_default(); + let caller = get_caller_address(); + let mut property: Property = world.read_model((property_id, game_id)); + + assert(property.owner == caller, 'Can only sell your property'); + + property.for_sale = true; + world.write_model(@property); + + true + } + + + fn mortgage_property(ref self: ContractState, mut property: Property) -> bool { + let mut world = self.world_default(); + + // Check the game is ongoing + let mut game: Game = world.read_model(property.game_id); + assert(game.status == GameStatus::Ongoing, 'Game has not started yet'); + + let caller = get_caller_address(); + let mut owner: GamePlayer = world.read_model((property.owner, property.game_id)); + + // Ensure caller owns property and it is not already mortgaged + assert(property.owner == caller, 'Not your property'); + assert(!property.is_mortgaged, 'Property already mortgaged'); + + // Mortgage: give owner half the cost + let amount: u256 = property.cost_of_property / 2; + owner.balance += amount; + + // Mark property as mortgaged + property.mortgage(caller); + + // Persist changes + world.write_model(@owner); + world.write_model(@property); + + true + } + + + fn unmortgage_property(ref self: ContractState, mut property: Property) -> bool { + let mut world = self.world_default(); + let caller = get_caller_address(); + + // Load game and ensure it's ongoing + let game: Game = world.read_model(property.game_id); + assert(game.status == GameStatus::Ongoing, 'Game has not started yet'); + + // Load owner + let mut owner: GamePlayer = world.read_model((property.owner, property.game_id)); + + // Assertions + assert(property.owner == caller, 'Only the owner can unmortgage'); + assert(property.is_mortgaged, 'Property is not mortgaged'); + + // Compute repayment (mortgage + interest) + let mortgage_amount: u256 = property.cost_of_property / 2; + let interest: u256 = mortgage_amount * 10 / 100; // 10% + let repay_amount: u256 = mortgage_amount + interest; + + assert(owner.balance >= repay_amount, 'Insufficient unmortgage'); + + // Pay the mortgage + owner.balance -= repay_amount; + + // Lift the mortgage flag + property.lift_mortgage(caller); + + // Persist changes + world.write_model(@owner); + world.write_model(@property); + + true + } + + + fn pay_rent(ref self: ContractState, mut property: Property) -> bool { + let mut world = self.world_default(); + let caller = get_caller_address(); + + let mut player: GamePlayer = world.read_model((caller, property.game_id)); + let mut owner: GamePlayer = world.read_model((property.owner, property.game_id)); + + // Validate game + let mut game: Game = world.read_model(property.game_id); + assert(game.status == GameStatus::Ongoing, 'Game not started'); + + // Basic checks + let zero_address: ContractAddress = contract_address_const::<0>(); + assert(property.owner != zero_address, 'Property unowned'); + assert(property.owner != caller, 'Cannot pay rent to yourself'); + assert(player.position == property.id, 'Not on property'); + assert(!property.is_mortgaged, 'No rent on mortgaged'); + + // Get dynamic counts + let railroads = self.count_owner_railroads(property.owner, property.game_id); + let utilities = self.count_owner_utilities(property.owner, property.game_id); + + // Calculate rent + let rent_amount = property + .get_rent_amount(railroads, utilities, player.dice_rolled.into()); + + assert(player.balance >= rent_amount, 'Insufficient funds'); + + // Transfer rent + player.balance -= rent_amount; + owner.balance += rent_amount; + + // Finish turn and persist + game = self.finish_turn(game); + + world.write_model(@game); + world.write_model(@player); + world.write_model(@owner); + world.write_model(@property); + + true + } + + + fn move_player(ref self: ContractState, game_id: u256, steps: u8) -> u8 { + let mut world = self.world_default(); + let caller = get_caller_address(); + 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'); + + // Move player + game_player = GamePlayerTrait::move(game_player, steps); + + game_player.dice_rolled = steps; + + if game_player.position >= 40 { + game_player.position %= 40; + game_player.balance += 200; + } + + let mut property = self.get_property(game_player.position, game_id); + property = self.handle_property_landing(game_player.clone(), property.clone()); + + // Update state + world.write_model(@game_player); + world.write_model(@game); + + game_player.position + } + + fn buy_property(ref self: ContractState, mut property: Property) -> bool { + // get the world + let mut world = self.world_default(); + // get the game and check it is ongoing + let mut found_game: Game = world.read_model(property.game_id); + assert!(found_game.status == GameStatus::Ongoing, "game has not started yet "); + + let caller = get_caller_address(); + + let mut player: GamePlayer = world.read_model((caller, property.game_id)); + let mut owner: GamePlayer = world.read_model((property.owner, property.game_id)); + + assert(player.position == property.id, 'wrong property'); + assert(player.game_id == owner.game_id, 'Not in the same game'); + assert(player.balance >= property.cost_of_property, 'insufficient funds'); + + // Transfer funds + player.balance -= property.cost_of_property; + owner.balance += property.cost_of_property; + + // Transfer ownership + property.owner = caller; + property.for_sale = false; + player.properties_owned.append(property.id); + + // Increment section or special counters + if property.property_type == PropertyType::RailRoad { + player.no_of_railways += 1; + } + if property.property_type == PropertyType::Utility { + player.no_of_utilities += 1; + } + match property.group_id { + 0 => {}, + 1 => player.no_section1 += 1, + 2 => player.no_section2 += 1, + 3 => player.no_section3 += 1, + 4 => player.no_section4 += 1, + 5 => player.no_section5 += 1, + 6 => player.no_section6 += 1, + 7 => player.no_section7 += 1, + 8 => player.no_section8 += 1, + _ => {}, + } + + // Finish turn + found_game = self.finish_turn(found_game); + + // Persist changes + world.write_model(@found_game); + world.write_model(@player); + world.write_model(@owner); + world.write_model(@property); + + true + } + + + fn buy_house_or_hotel(ref self: ContractState, mut property: Property) -> bool { + let mut world = self.world_default(); + let caller = get_caller_address(); + let mut player: GamePlayer = world.read_model((caller, property.game_id)); + + assert(property.owner == caller, 'Only the owner can develop'); + assert(!property.is_mortgaged, 'Property is mortgaged'); + assert(property.development < 5, 'Maximum development reached'); + + // ✅ Check owns full set + let owns_entire_group = match property.group_id { + 0 => false, + 1 => player.no_section1 == 2, + 2 => player.no_section2 == 3, + 3 => player.no_section3 == 3, + 4 => player.no_section4 == 3, + 5 => player.no_section5 == 3, + 6 => player.no_section6 == 3, + 7 => player.no_section7 == 3, + 8 => player.no_section8 == 2, + _ => false, + }; + assert!(owns_entire_group, "Must own all properties in the group to build"); + + // ✅ Enforce even building + let group_properties: Array = self + .get_properties_by_group(property.group_id, property.game_id); + + let mut i = 0; + while i < group_properties.len() { + let prop = group_properties[i]; + if *prop.id != property.id { + assert!( + *prop.development >= property.development, + "Must build evenly: other properties are under-developed", + ); + } + i += 1; + }; + + // ✅ Passed checks, build + let cost: u256 = property.cost_of_house; + assert(player.balance >= cost, 'Insufficient balance'); + + player.balance -= cost; + property.development += 1; + + if property.development < 5 { + player.total_houses_owned += 1; + } else { + player.total_hotels_owned += 1; + } + + world.write_model(@property); + world.write_model(@player); + + true + } + + + // fn offer_trade( + // ref self: ContractState, + // game_id: u256, + // to: ContractAddress, + // offered_property_ids: Array, + // requested_property_ids: Array, + // cash_offer: u256, + // cash_request: u256, + // ) { + // let mut world = self.world_default(); + // let caller = get_caller_address(); + + // // Validate trade parameters + // assert(offered_property_ids.len() > 0 || cash_offer > 0, 'No properties or cash + // offered'); + // assert(requested_property_ids.len() > 0 || cash_request > 0, 'No properties or cash + // requested'); + + // // Ensure the recipient is a valid player in the game + // let recipient_username = self.get_username_from_address(to); + // assert(recipient_username != 0, 'Recipient not registered'); + + // // Create and store the trade offer + // let trade_id = world.get_next_trade_id(); + // let trade_offer = TradeOffer { + // trade_id, + // from: caller, + // to, + // offered_property_ids, + // requested_property_ids, + // cash_offer, + // cash_request, + // status: TradeStatus::Pending, + // }; + + fn sell_house_or_hotel(ref self: ContractState, property_id: u8, game_id: u256) -> bool { + let mut world = self.world_default(); + let caller = get_caller_address(); + let mut property: Property = world.read_model((property_id, game_id)); + let contract_address = get_contract_address(); + + assert(property.owner == caller, 'Only the owner '); + assert(property.development > 0, 'No houses to sell'); + + let refund: u256 = property.cost_of_house / 2; + + // self.transfer_from(contract_address, caller, game_id, refund);/ + + property.development -= 1; + + world.write_model(@property); + + true + } + + + fn retrieve_game(self: @ContractState, game_id: u256) -> Game { + // Get default world + let mut world = self.world_default(); + //get the game state + let game: Game = world.read_model(game_id); + game + } + + fn transfer_from( + ref self: ContractState, mut from: GamePlayer, mut to: GamePlayer, amount: u256, + ) -> Array { + let mut world = self.world_default(); + + assert(from.game_id == to.game_id, 'Not in the same game'); + assert(from.balance >= amount, 'insufficient funds'); + + from.balance -= amount; + to.balance += amount; + + let mut game_players: Array = array![]; + game_players.append(from); + game_players.append(to); + + game_players + } + + + fn mint(ref self: ContractState, recepient: ContractAddress, game_id: u256, amount: u256) { + let mut world = self.world_default(); + let mut player: GamePlayer = world.read_model((recepient, game_id)); + player.balance += amount; + world.write_model(@player); + } + + + fn get_players_balance( + self: @ContractState, player: ContractAddress, game_id: u256, + ) -> u256 { + let world = self.world_default(); + + let players_balance: GameBalance = world.read_model((player, game_id)); + players_balance.balance + } + + fn retrieve_player(self: @ContractState, addr: ContractAddress) -> Player { + // Get default world + let mut world = self.world_default(); + let player: Player = world.read_model(addr); + + player + } + + fn retrieve_game_player( + self: @ContractState, addr: ContractAddress, game_id: u256, + ) -> GamePlayer { + // Get default world + let mut world = self.world_default(); + let player: GamePlayer = world.read_model((addr, game_id)); + + player + } + + fn is_game_started(self: @ContractState, game_id: u256) -> u8 { + let mut world = self.world_default(); + let mut game: Game = world.read_model(game_id); + let mut stat: u8 = 10; + if (game.status == GameStatus::Ongoing) { + stat = 1 + }; + if (game.status == GameStatus::Ended) { + stat = 0 + }; + + if (game.status == GameStatus::Pending) { + stat = 2 + }; + stat + } + + fn finish_turn(ref self: ContractState, mut game: Game) -> Game { + let mut world = self.world_default(); + let caller = get_caller_address(); + let mut index = 0; + let mut current_index = 0; + let players_len = game.game_players.len(); + + while index < players_len { + let player = game.game_players.at(index); + if *player == caller { + current_index = index; + break; + } + index += 1; + }; + + let next_index = (current_index + 1) % players_len; + game.next_player = *game.game_players.at(next_index); + + world.write_model(@game); + game + } + + fn handle_chance(ref self: ContractState, game_id: u256, random_index: u32) -> ByteArray { + let mut world = self.world_default(); + let mut game: Game = world.read_model(game_id); + + let mut len = game.chance.len(); + if len <= 1 { + game.chance = self.generate_chance_deck(); + len = game.chance.len(); + world.write_model(@game); + } + + let draw_index = random_index % len; + + let card = game.chance[draw_index].clone(); + + // Build new deck excluding drawn card + let mut new_deck = array![]; + let mut i = 0; + while i < len { + if i != draw_index { + new_deck.append(game.chance[i].clone()); + } + i += 1; + }; + game.chance = new_deck; + world.write_model(@game); + + card + } + fn handle_community_chest( + ref self: ContractState, game_id: u256, random_index: u32, + ) -> ByteArray { + let mut world = self.world_default(); + let mut game: Game = world.read_model(game_id); + + let mut len = game.community.len(); + if len <= 1 { + game.community = self.generate_community_chest_deck(); + len = game.community.len(); + world.write_model(@game); + } + + let draw_index = random_index % len; + + let card = game.community[draw_index].clone(); + + let mut new_deck = array![]; + let mut i = 0; + while i < len { + if i != draw_index { + new_deck.append(game.community[i].clone()); + } + i += 1; + }; + game.community = new_deck; + world.write_model(@game); + + card + } + + fn process_chance_card( + ref self: ContractState, mut game: Game, mut player: GamePlayer, card: ByteArray, + ) -> (Game, GamePlayer) { + // We'll decode by matching text. + // NOTE: If you have enums or ids for cards, it's cleaner. With ByteArray, compare + // strings. + let mut world = self.world_default(); + let property = self.get_property(player.position, game.id); + assert(property.property_type == PropertyType::Chance, 'not on chance'); + if card == "Advance to Go (Collect $200)" { + player.position = 0; + player.balance += 200; + } else if card == "Advance to MakerDAO Avenue - If you pass Go, collect $200" { + if player.position > 24 { // suppose Illinois is tile 24 + player.balance += 200; + } + player.position = 24; + } else if card == "Advance to Arbitrium Avenue - If you pass Go, collect $200" { + if player.position > 11 { + player.balance += 200; + } + player.position = 11; + } else if card == "Advance token to nearest Utility. Pay 10x dice." { + let mut pos: Array = array![]; + pos.append(12); + pos.append(28); + let utility_pos = self.find_nearest(player.position, pos); + player.position = utility_pos; + let rent: u256 = 10 * player.dice_rolled.into(); + player.balance -= rent; + } else if card == "Advance token to nearest Railroad. Pay 2x rent." { + let mut pos: Array = array![]; + pos.append(5); + pos.append(15); + pos.append(25); + pos.append(35); + let rail_pos = self.find_nearest(player.position, pos); + player.position = rail_pos; + let rail = self.get_property(rail_pos, game.clone().id); + let mut rail_owner: GamePlayer = self.retrieve_game_player(rail.owner, game.id); + let railroads = self.count_owner_railroads(property.owner, property.game_id); + let utilities = self.count_owner_utilities(property.owner, property.game_id); + let mut rent_amount = rail + .get_rent_amount(railroads, utilities, player.dice_rolled.into()); + if (rail.owner == get_contract_address()) { + rent_amount = 0; + } + player.balance -= rent_amount; + rail_owner.balance += rent_amount; + + world.write_model(@rail_owner); + } else if card == "Bank pays you dividend of $50" { + player.balance += 50; + } else if card == "Get out of Jail Free" { + player.chance_jail_card = true; + } else if card == "Go Back 3 Spaces" { + if player.position < 4 { + player.position = 39 - (4 - player.position); + } else { + player.position -= 3; + } + } else if card == "Go to Jail" { + player.position = 11; // suppose jail is tile 11 + player.jailed = true; + } else if card == "Make general repairs - $25 house, $100 hotel" { + let houses = player.total_houses_owned; + let hotels = player.total_hotels_owned; + let cost = (25 * houses) + (100 * hotels); + player.balance -= cost.into(); + } else if card == "Pay poor tax of $15" { + player.balance -= 15; + } else if card == "Take a trip to IPFS Railroad" { + if player.position > 5 { + player.balance += 200; + } + player.position = 5; // reading railroad + } else if card == "Take a walk on the Bitcoin Lane" { + player.position = 39; + } else if card == "Speeding fine $200" { + player.balance -= 200; + } else if card == "Building loan matures - collect $150" { + player.balance += 150; + } + game = self.finish_turn(game); + world.write_model(@player); + world.write_model(@game); + (game, player) + } + + fn process_community_chest_card( + ref self: ContractState, mut game: Game, mut player: GamePlayer, card: ByteArray, + ) -> (Game, GamePlayer) { + let mut world = self.world_default(); + let property = self.get_property(player.position, game.id); + assert( + property.property_type == PropertyType::CommunityChest, 'not on community chest', + ); + if card == "Advance to Go (Collect $200)" { + player.position = 1; + player.balance += 200; + } else if card == "Bank error in your favor - Collect $200" { + player.balance += 200; + } else if card == "Doctor fee - Pay $50" { + player.balance -= 50; + } else if card == "From sale of stock - collect $50" { + player.balance += 50; + } 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.jailed = true; + } else if card == "Grand Opera Night - collect $50 from every player" { + let mut i = 0; + while i < game.game_players.len() { + let addr = *game.game_players[i]; + if addr != player.address { + let mut other_player: GamePlayer = self.retrieve_game_player(addr, game.id); + other_player.balance -= 50; + player.balance += 50; + world.write_model(@other_player); + } + i += 1; + } + } else if card == "Holiday Fund matures - Receive $100" { + player.balance += 100; + } else if card == "Income tax refund - Collect $20" { + player.balance += 20; + } else if card == "Life insurance matures - Collect $100" { + player.balance += 100; + } else if card == "Pay hospital fees of $100" { + player.balance -= 100; + } else if card == "Pay school fees of $150" { + player.balance -= 150; + } else if card == "Receive $25 consultancy fee" { + player.balance += 25; + } else if card == "Street repairs - $40 per house, $115 per hotel" { + let houses = player.total_houses_owned; + let hotels = player.total_hotels_owned; + let cost = (40 * houses) + (115 * hotels); + player.balance -= cost.into(); + } else if card == "Won second prize in beauty contest - Collect $10" { + player.balance += 10; + } else if card == "You inherit $100" { + player.balance += 100; + } + + game = self.finish_turn(game); + world.write_model(@player); + world.write_model(@game); + (game, player) + } + } + + #[generate_trait] + impl InternalImpl of InternalTrait { + /// Use the default namespace "dojo_starter". This function is handy since the ByteArray + /// can't be const. + fn world_default(self: @ContractState) -> dojo::world::WorldStorage { + self.world(@"blockopoly") + } + + fn generate_chance_deck(ref self: ContractState) -> Array { + let mut deck: Array = array![]; + + deck.append("Advance to Go (Collect $200)"); + deck.append("Advance to MakerDAO Avenue - If you pass Go, collect $200"); + deck.append("Advance to Arbitrium Avenue - If you pass Go, collect $200"); + deck.append("Advance token to nearest Utility. Pay 10x dice."); + deck.append("Advance token to nearest Railroad. Pay 2x rent."); + deck.append("Bank pays you dividend of $50"); + deck.append("Get out of Jail Free"); + deck.append("Go Back 3 Spaces"); + deck.append("Go to Jail dirctly do not pass Go do not collect $200"); + deck.append("Make general repairs - $25 house, $100 hotel"); + deck.append("Pay poor tax of $15"); + deck.append("Take a trip to Reading Railroad"); + deck.append("Take a walk on the Bitcoin Lane"); + deck.append("Speeding fine $200"); + deck.append("Building loan matures - collect $150"); + + // self.shuffle_array(deck); + + deck + } + + // Count how many railroads the owner has + fn count_owner_railroads( + ref self: ContractState, owner: ContractAddress, game_id: u256, + ) -> u8 { + let mut count = 0; + let mut i = 1; + while i < 41_u32 { + let prop: Property = self.world_default().read_model((i, game_id)); + if prop.owner == owner && prop.property_type == PropertyType::RailRoad { + count += 1; + } + i += 1; + }; + count + } + + // Count how many utilities the owner has + fn count_owner_utilities( + ref self: ContractState, owner: ContractAddress, game_id: u256, + ) -> u8 { + let mut count = 0; + let mut i = 1; + while i < 41_u32 { + let prop: Property = self.world_default().read_model((i, game_id)); + if prop.owner == owner && prop.property_type == PropertyType::Utility { + count += 1; + } + i += 1; + }; + count + } + + + fn generate_chance_indices(ref self: ContractState) -> Array { + let mut indices: Array = array![]; + let cards = self.generate_chance_deck(); + let n = cards.len(); + let mut i = 0; + while i < n { + indices.append(i); + i += 1; + }; + indices + } + + fn handle_chance(ref self: ContractState, game_id: u256, random_index: u32) -> ByteArray { + let mut world = self.world_default(); + let mut game: Game = world.read_model(game_id); + + let len = game.chance.len(); + if len == 0 { + game.chance = self.generate_chance_deck(); // returns Array + world.write_model(@game); + } + let draw_index = (random_index % (len)); + + // Directly get the card ByteArray from game.chance (no further indexing needed) + let card = game.chance[draw_index].clone(); + + // Build a new deck without the drawn card + let mut new_deck = array![]; + let mut i = 0; + while i < len { + if i != draw_index { + new_deck.append(game.chance[i].clone()); + } + i += 1; + }; + game.chance = new_deck.clone(); + + world.write_model(@game); + + card + } + + + fn find_nearest(ref self: ContractState, player_pos: u8, utilities: Array) -> u8 { + let board_size: u8 = 40; + + let mut nearest_pos: u8 = *utilities[0]; + let mut smallest_distance = if *utilities[0] >= player_pos { + *utilities[0] - player_pos + } else { + board_size - (player_pos - *utilities[0]) + }; + + let len = utilities.len(); + let mut i = 1; + while i < len { + let utility_pos = *utilities[i]; + let distance = if utility_pos >= player_pos { + utility_pos - player_pos + } else { + board_size - (player_pos - utility_pos) + }; + + if distance < smallest_distance { + smallest_distance = distance; + nearest_pos = utility_pos; + } + i += 1; + }; + + nearest_pos + } + + + fn get_properties_by_group( + ref self: ContractState, group_id: u8, game_id: u256, + ) -> Array { + let mut world = self.world_default(); + let mut group_properties: Array = array![]; + + let mut i = 0; // defaults to felt252 + while i < 41_u32 { + let prop: Property = world.read_model((i, game_id)); + if prop.group_id == group_id { + group_properties.append(prop); + } + i += 1; + }; + + group_properties + } + + + fn generate_board_tiles(ref self: ContractState, game_id: u256) { + let mut world = self.world_default(); + let contract_address = get_contract_address(); + let bank: GamePlayer = world.read_model((contract_address, game_id)); + + self + .generate_properties( + 0, + game_id, + 'Go', + 0, + PropertyType::Go, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 1, + game_id, + 'Axone Avenue', + 60, + PropertyType::Property, + 2, + 10, + 30, + 90, + 160, + 250, + 50, + false, + 1, + bank.address, + ); + self + .generate_properties( + 2, + game_id, + 'Community Chest', + 0, + PropertyType::CommunityChest, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 3, + game_id, + 'Onlydust Avenue', + 60, + PropertyType::Property, + 4, + 20, + 60, + 180, + 320, + 450, + 50, + false, + 1, + bank.address, + ); + self + .generate_properties( + 4, + game_id, + 'Luxury Tax', + 100, + PropertyType::Tax, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 5, + game_id, + 'IPFS Railroad', + 200, + PropertyType::RailRoad, + 25, + 50, + 100, + 200, + 400, + 0, + 0, + false, + 0, + bank.address, + ); + + self + .generate_properties( + 6, + game_id, + 'ZkSync Lane', + 100, + PropertyType::Property, + 6, + 30, + 90, + 270, + 400, + 550, + 50, + false, + 2, + bank.address, + ); + self + .generate_properties( + 7, + game_id, + 'Chance', + 0, + PropertyType::Chance, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 8, + game_id, + 'Starknet Lane', + 100, + PropertyType::Property, + 6, + 30, + 90, + 270, + 400, + 550, + 50, + false, + 2, + bank.address, + ); + self + .generate_properties( + 9, + game_id, + 'Linea Lane', + 120, + PropertyType::Property, + 8, + 40, + 100, + 300, + 450, + 600, + 50, + false, + 2, + bank.address, + ); + + self + .generate_properties( + 10, + game_id, + 'Visiting Jail', + 0, + PropertyType::VisitingJail, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 11, + game_id, + 'Arbitrium Avenue', + 140, + PropertyType::Property, + 10, + 50, + 150, + 450, + 625, + 750, + 100, + false, + 3, + bank.address, + ); + self + .generate_properties( + 12, + game_id, + 'Chainlink Power Plant', + 150, + PropertyType::Utility, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 13, + game_id, + 'Optimistic Avenue', + 140, + PropertyType::Property, + 10, + 50, + 150, + 450, + 625, + 750, + 100, + false, + 3, + bank.address, + ); + self + .generate_properties( + 14, + game_id, + 'Base Avenue', + 160, + PropertyType::Property, + 12, + 60, + 180, + 500, + 700, + 900, + 100, + false, + 3, + bank.address, + ); + self + .generate_properties( + 15, + game_id, + 'Pinata Railroad', + 200, + PropertyType::RailRoad, + 25, + 50, + 100, + 200, + 400, + 0, + 0, + false, + 0, + bank.address, + ); + + self + .generate_properties( + 16, + game_id, + 'Near Lane', + 200, + PropertyType::Property, + 14, + 70, + 200, + 550, + 750, + 950, + 100, + false, + 4, + bank.address, + ); + self + .generate_properties( + 17, + game_id, + 'Community Chest', + 0, + PropertyType::CommunityChest, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 18, + game_id, + 'Cosmos Lane', + 180, + PropertyType::Property, + 14, + 70, + 200, + 550, + 750, + 950, + 100, + false, + 4, + bank.address, + ); + self + .generate_properties( + 19, + game_id, + 'Polkadot Lane', + 180, + PropertyType::Property, + 14, + 70, + 200, + 550, + 750, + 950, + 100, + false, + 4, + bank.address, + ); + + self + .generate_properties( + 20, + game_id, + 'Free Parking', + 0, + PropertyType::FreeParking, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 21, + game_id, + 'Dune Lane', + 220, + PropertyType::Property, + 18, + 90, + 250, + 700, + 875, + 1050, + 150, + false, + 5, + bank.address, + ); + self + .generate_properties( + 22, + game_id, + 'Chance', + 0, + PropertyType::Chance, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 23, + game_id, + 'Uniswap Avenue', + 220, + PropertyType::Property, + 18, + 90, + 250, + 700, + 875, + 1050, + 150, + false, + 5, + bank.address, + ); + self + .generate_properties( + 24, + game_id, + 'MakerDAO Avenue', + 240, + PropertyType::Property, + 20, + 100, + 300, + 750, + 925, + 1100, + 150, + false, + 5, + bank.address, + ); + self + .generate_properties( + 25, + game_id, + 'OpenZeppelin Railroad', + 200, + PropertyType::RailRoad, + 25, + 50, + 100, + 200, + 400, + 0, + 0, + false, + 0, + bank.address, + ); + + self + .generate_properties( + 26, + game_id, + 'Aave Avenue', + 260, + PropertyType::Property, + 22, + 110, + 330, + 800, + 975, + 1150, + 150, + false, + 6, + bank.address, + ); + self + .generate_properties( + 27, + game_id, + 'Lisk Lane', + 260, + PropertyType::Property, + 22, + 110, + 330, + 800, + 975, + 1150, + 150, + false, + 6, + bank.address, + ); + self + .generate_properties( + 28, + game_id, + 'Graph Water Works', + 150, + PropertyType::Utility, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 29, + game_id, + 'Rootstock Lane', + 260, + PropertyType::Property, + 22, + 110, + 330, + 800, + 975, + 1150, + 150, + false, + 6, + bank.address, + ); + + self + .generate_properties( + 30, + game_id, + 'Go To Jail', + 0, + PropertyType::Jail, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 31, + game_id, + 'Rootstock Lane', + 300, + PropertyType::Property, + 26, + 130, + 390, + 900, + 1100, + 1275, + 200, + false, + 7, + bank.address, + ); + self + .generate_properties( + 32, + game_id, + 'Ark Lane', + 280, + PropertyType::Property, + 26, + 130, + 390, + 900, + 1100, + 1275, + 200, + false, + 7, + bank.address, + ); + self + .generate_properties( + 33, + game_id, + 'Community Chest', + 0, + PropertyType::CommunityChest, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 34, + game_id, + 'Avalanche Avenue', + 300, + PropertyType::Property, + 26, + 130, + 390, + 900, + 1100, + 1275, + 200, + false, + 7, + bank.address, + ); + self + .generate_properties( + 35, + game_id, + 'Cartridge Railroad', + 200, + PropertyType::RailRoad, + 25, + 50, + 100, + 200, + 400, + 0, + 0, + false, + 0, + bank.address, + ); + + self + .generate_properties( + 36, + game_id, + 'Chance', + 0, + PropertyType::Chance, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 37, + game_id, + 'Solana Drive', + 350, + PropertyType::Property, + 35, + 175, + 500, + 1100, + 1300, + 1500, + 200, + false, + 8, + bank.address, + ); + self + .generate_properties( + 38, + game_id, + 'Luxury Tax', + 100, + PropertyType::Tax, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + false, + 0, + bank.address, + ); + self + .generate_properties( + 39, + game_id, + 'Ethereum Avenue', + 400, + PropertyType::Property, + 50, + 200, + 600, + 1400, + 1700, + 2000, + 200, + false, + 8, + bank.address, + ); + } + + + fn generate_community_chest_deck(ref self: ContractState) -> Array { + let mut deck: Array = array![]; + + deck.append("Advance to Go (Collect $200)"); + deck.append("Bank error in your favor - Collect $200"); + deck.append("Doctor fee - Pay $50"); + deck.append("From sale of stock - collect $50"); + deck.append("Get Out of Jail Free"); + deck.append("Go to Jail"); + deck.append("Grand Opera Night - collect $50 from every player"); + deck.append("Holiday Fund matures - Receive $100"); + deck.append("Income tax refund - Collect $20"); + deck.append("Life insurance matures - Collect $100"); + deck.append("Pay hospital fees of $100"); + deck.append("Pay school fees of $150"); + deck.append("Receive $25 consultancy fee"); + deck.append("Street repairs - $40 per house, $115 per hotel"); + deck.append("Won second prize in beauty contest - Collect $10"); + deck.append("You inherit $100"); + + // self.shuffle_array(deck); + + deck + } + + fn try_join_symbol( + ref self: ContractState, + mut game: Game, + symbol: PlayerSymbol, + username: felt252, + game_id: u256, + ) { + match symbol { + PlayerSymbol::Hat => { + assert(game.player_hat == 0, 'HAT already selected'); + game.player_hat = username; + }, + PlayerSymbol::Car => { + assert(game.player_car == 0, 'CAR already selected'); + game.player_car = username; + }, + PlayerSymbol::Dog => { + assert(game.player_dog == 0, 'DOG already selected'); + game.player_dog = username; + }, + PlayerSymbol::Thimble => { + assert(game.player_thimble == 0, 'THIMBLE already selected'); + game.player_thimble = username; + }, + PlayerSymbol::Iron => { + assert(game.player_iron == 0, 'IRON already selected'); + game.player_iron = username; + }, + PlayerSymbol::Battleship => { + assert(game.player_battleship == 0, 'BATTLESHIP already selected'); + game.player_battleship = username; + }, + PlayerSymbol::Boot => { + assert(game.player_boot == 0, 'BOOT already selected'); + game.player_boot = username; + }, + PlayerSymbol::Wheelbarrow => { + assert(game.player_wheelbarrow == 0, 'WHEELBARROW already selected'); + game.player_wheelbarrow = username; + }, + } + } + + fn count_joined_players(ref self: ContractState, mut game: Game) -> u8 { + let mut count: u8 = 0; + + if game.player_hat != 0 { + count += 1; // self.transfer_funds(caller, property.rent_site_only); + // self.credit_owner(property.owner, property.rent_site_only); + } + if game.player_car != 0 { + count += 1; + } + if game.player_dog != 0 { + count += 1; + } + if game.player_thimble != 0 { + count += 1; + } + if game.player_iron != 0 { + count += 1; + } + if game.player_battleship != 0 { + count += 1; + } + if game.player_boot != 0 { + count += 1; + } + if game.player_wheelbarrow != 0 { + count += 1; + } + + count + } + + fn assert_player_not_already_joined( + ref self: ContractState, + game: Game, + username: felt252 // self.transfer_funds(caller,// self.transfer_funds(caller, property.rent_site_only); + // self.credit_owner(property.owner, property.rent_site_only); property.rent_site_only); + // self.credit_owner(property.owner, property.rent_site_only); + ) { + assert(game.player_hat != username, 'ALREADY SELECTED HAT'); + assert(game.player_car != username, 'ALREADY SELECTED CAR'); + assert(game.player_dog != username, 'ALREADY SELECTED DOG'); + assert(game.player_thimble != username, 'ALREADY SELECTED THIMBLE'); + assert(game.player_iron != username, 'ALREADY SELECTED IRON'); + assert(game.player_battleship != username, 'ALREADY SELECTED BATTLESHIP'); + assert(game.player_boot != username, 'ALREADY SELECTED BOOT'); + assert(game.player_wheelbarrow != username, 'ALREADY SELECTED WHEELBARROW'); + } + + fn handle_property_landing( + ref self: ContractState, player: GamePlayer, mut property: Property, + ) -> Property { + let caller = get_caller_address(); + let bank_address = get_contract_address(); + + match property.property_type { + PropertyType::CommunityChest => { + println!( + "Player {:?} landed on Community Chest '{}'. Drawing a card...", + caller, + property.name, + ); + // you could call self.draw_community_chest_card() here + }, + PropertyType::Chance => { + println!( + "Player {:?} landed on Chance '{}'. Drawing a card...", + caller, + property.name, + ); + // you could call self.draw_chance_card() here + }, + _ => { + if property.owner == bank_address { + println!( + "This property '{}' is owned by the bank. It costs {}.", + property.name, + property.cost_of_property, + ); + } else if property.owner != caller { + // Owned by someone else + let owner_railroads = self + .count_owner_railroads(property.owner, property.game_id); + let owner_utilities = self + .count_owner_utilities(property.owner, property.game_id); + + let rent_amount = property + .get_rent_amount( + owner_railroads, owner_utilities, player.dice_rolled.into(), + ); + + match property.property_type { + PropertyType::RailRoad => { + println!( + "This railroad '{}' is owned by {:?}. Player {:?} must pay rent: {}.", + property.name, + property.owner, + caller, + rent_amount, + ); + }, + PropertyType::Utility => { + println!( + "This utility '{}' is owned by {:?}. Player {:?} must pay rent: {}.", + property.name, + property.owner, + caller, + rent_amount, + ); + }, + PropertyType::Property => { + println!( + "This property '{}' is owned by {:?}. Player {:?} must pay rent: {}.", + property.name, + property.owner, + caller, + rent_amount, + ); + }, + _ => { + println!( + "This space '{}' is owned by {:?}. Player {:?} may owe rent: {}.", + property.name, + property.owner, + caller, + rent_amount, + ); + }, + } + } else { + // Player owns this property + println!( + "Player {:?} landed on their own property '{}'.", caller, property.name, + ); + } + }, + } + + property + } + } + + + #[generate_trait] + impl PlayerGameBalanceImpl of IPlayerGameBalance { + fn check_if_player_is_capable_of_trans( + ref self: ContractState, amount: u256, balance: u256, + ) { + assert!(amount <= balance, "Insufficient balance"); + } + } + + #[generate_trait] + impl BoardTilesImpl of IBoardTiles { + fn generate_properties( + ref self: ContractState, + id: u8, + game_id: u256, + name: felt252, + cost_of_property: u256, + property_type: PropertyType, + rent_site_only: u256, + rent_one_house: u256, + rent_two_houses: u256, + rent_three_houses: u256, + rent_four_houses: u256, + cost_of_house: u256, + rent_hotel: u256, + is_mortgaged: bool, + group_id: u8, + owner: ContractAddress, + ) { + let mut world = self.world_default(); + let mut property: Property = world.read_model((id, game_id)); + + property = + PropertyTrait::new( + id, + game_id, + name, + cost_of_property, + property_type, + rent_site_only, + rent_one_house, + rent_two_houses, + rent_three_houses, + rent_four_houses, + rent_hotel, + cost_of_house, + group_id, + owner, + ); + + let property_to_id: PropertyToId = PropertyToId { name, id }; + let id_to_property: IdToProperty = IdToProperty { id, name }; + + world.write_model(@property); + world.write_model(@property_to_id); + world.write_model(@id_to_property); + } + + fn create_new_game_id(ref self: ContractState) -> u256 { + let mut world = self.world_default(); + let mut game_counter: GameCounter = world.read_model('v0'); + let new_val = game_counter.current_val + 1; + game_counter.current_val = new_val; + world.write_model(@game_counter); + new_val + } + } +} + diff --git a/src/systems/world.cairo b/src/systems/world.cairo deleted file mode 100644 index 3b44d0e..0000000 --- a/src/systems/world.cairo +++ /dev/null @@ -1,879 +0,0 @@ -use dojo_starter::model::game_model::{ - GameMode, GameBalance, Game, GameTrait, GameCounter, GameStatus, -}; -use dojo_starter::model::player_model::{ - Player, PlayerSymbol, UsernameToAddress, AddressToUsername, PlayerTrait, -}; -use dojo_starter::interfaces::IWorld::IWorld; - -// dojo decorator -#[dojo::contract] -pub mod world { - use super::{ - IWorld, Player, GameMode, GameBalance, PlayerSymbol, Game, GameTrait, UsernameToAddress, - AddressToUsername, PlayerTrait, GameCounter, GameStatus, - }; - use dojo_starter::model::property_model::{Property, PropertyTrait, PropertyToId, IdToProperty}; - use core::array::Array; - use starknet::{ - ContractAddress, get_caller_address, get_block_timestamp, contract_address_const, - get_contract_address, - }; - - - use dojo::model::{ModelStorage}; - use dojo::event::EventStorage; - use origami_random::dice::{Dice, DiceTrait}; - - - #[derive(Copy, Drop, Serde)] - #[dojo::event] - pub struct GameCreated { - #[key] - pub game_id: u256, - pub timestamp: u64, - } - - #[derive(Copy, Drop, Serde)] - #[dojo::event] - pub struct PlayerCreated { - #[key] - pub username: felt252, - #[key] - pub player: ContractAddress, - pub timestamp: u64, - } - - #[derive(Copy, Drop, Serde)] - #[dojo::event] - pub struct GameStarted { - #[key] - pub game_id: u256, - pub timestamp: u64, - } - - #[derive(Copy, Drop, Serde)] - #[dojo::event] - pub struct PlayerJoined { - #[key] - pub game_id: u256, - #[key] - pub username: felt252, - pub timestamp: u64, - } - - #[derive(Copy, Drop, Serde)] - #[dojo::event] - pub struct PropertyPurchased { - #[key] - pub game_id: u256, - #[key] - pub property_id: u8, - pub buyer: ContractAddress, - pub seller: ContractAddress, - pub amount: u256, - pub timestamp: u64, - } - - #[derive(Copy, Drop, Serde)] - #[dojo::event] - pub struct PropertyMortgaged { - #[key] - pub game_id: u256, - #[key] - pub property_id: u8, - pub owner: ContractAddress, - pub amount_received: u256, - pub timestamp: u64, - } - - #[derive(Copy, Drop, Serde)] - #[dojo::event] - pub struct PropertyUnmortgaged { - #[key] - pub game_id: u256, - #[key] - pub property_id: u8, - pub owner: ContractAddress, - pub amount_paid: u256, - pub timestamp: u64, - } - - #[derive(Copy, Drop, Serde)] - #[dojo::event] - pub struct RentCollected { - #[key] - pub game_id: u256, - #[key] - pub property_id: u8, - pub from_player: ContractAddress, - pub to_player: ContractAddress, - pub amount: u256, - pub development_level: u8, - pub timestamp: u64, - } - - #[derive(Copy, Drop, Serde)] - #[dojo::event] - pub struct PropertyDeveloped { - #[key] - pub game_id: u256, - #[key] - pub property_id: u8, - pub owner: ContractAddress, - pub development_level: u8, - pub cost: u256, - pub timestamp: u64, - } - - - #[abi(embed_v0)] - impl WorldImpl of IWorld { - fn get_username_from_address(self: @ContractState, address: ContractAddress) -> felt252 { - let mut world = self.world_default(); - - let address_map: AddressToUsername = world.read_model(address); - - address_map.username - } - fn register_new_player( - ref self: ContractState, - username: felt252, - is_bot: bool, - player_symbol: PlayerSymbol, - initial_balance: u256, - ) { - assert(!is_bot, 'Bot detected'); - let mut world = self.world_default(); - - let caller: ContractAddress = get_caller_address(); - - let zero_address: ContractAddress = contract_address_const::<0x0>(); - - // Validate username - assert(username != 0, 'USERNAME CANNOT BE ZERO'); - - // Check if the player already exists (ensure username is unique) - let existing_player: UsernameToAddress = world.read_model(username); - assert(existing_player.address == zero_address, 'USERNAME ALREADY TAKEN'); - - // Ensure player cannot update username by calling this function - let existing_username = self.get_username_from_address(caller); - - assert(existing_username == 0, 'USERNAME ALREADY CREATED'); - - let new_player: Player = PlayerTrait::new( - username, caller, is_bot, player_symbol, initial_balance, - ); - let username_to_address: UsernameToAddress = UsernameToAddress { - username, address: caller, - }; - let address_to_username: AddressToUsername = AddressToUsername { - address: caller, username, - }; - - world.write_model(@new_player); - world.write_model(@username_to_address); - world.write_model(@address_to_username); - world - .emit_event( - @PlayerCreated { username, player: caller, timestamp: get_block_timestamp() }, - ); - } - - fn create_new_game_id(ref self: ContractState) -> u256 { - let mut world = self.world_default(); - let mut game_counter: GameCounter = world.read_model('v0'); - let new_val = game_counter.current_val + 1; - game_counter.current_val = new_val; - world.write_model(@game_counter); - new_val.try_into().unwrap() - } - - fn get_players_balance( - ref self: ContractState, player: ContractAddress, game_id: u256, - ) -> u256 { - let world = self.world_default(); - - let players_balance: GameBalance = world.read_model((player, game_id)); - players_balance.balance - } - - fn get_properties_owned_by_player( - ref self: ContractState, player: ContractAddress, game_id: u256, - ) -> Array { - let world = self.world_default(); - let mut owned_properties = ArrayTrait::new(); - - // Check all 40 properties (standard Monopoly board) - let mut property_id: u8 = 1; - loop { - if property_id > 40 { - break; - } - - let property: Property = world.read_model((property_id, game_id)); - if property.owner == player { - owned_properties.append(property_id); - } - - property_id += 1; - }; - - owned_properties - } - - fn get_properties_by_group( - ref self: ContractState, group_id: u8, game_id: u256, - ) -> Array { - let world = self.world_default(); - let mut group_properties = ArrayTrait::new(); - - // Check all 40 properties - let mut property_id: u8 = 1; - loop { - if property_id > 40 { - break; - } - - let property: Property = world.read_model((property_id, game_id)); - if property.group_id == group_id { - group_properties.append(property_id); - } - - property_id += 1; - }; - - group_properties - } - - fn has_monopoly( - ref self: ContractState, player: ContractAddress, group_id: u8, game_id: u256, - ) -> bool { - let world = self.world_default(); - let group_properties = self.get_properties_by_group(group_id, game_id); - - // Check if player owns all properties in the group - let mut i = 0; - loop { - if i >= group_properties.len() { - break true; - } - - let property_id = *group_properties.at(i); - let property: Property = world.read_model((property_id, game_id)); - - if property.owner != player { - break false; - } - - i += 1; - } - } - - fn collect_rent_with_monopoly( - ref self: ContractState, property_id: u8, game_id: u256, - ) -> bool { - let mut world = self.world_default(); - let caller = get_caller_address(); - let property: Property = world.read_model((property_id, game_id)); - let zero_address: ContractAddress = contract_address_const::<0>(); - - assert(property.owner != zero_address, 'Property is unowned'); - assert(property.owner != caller, 'You cannot pay rent to yourself'); - assert(property.is_mortgaged == false, 'No rent on mortgaged properties'); - - let mut rent_amount: u256 = 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, - 5 => property.rent_hotel, - _ => panic!("Invalid development level"), - }; - - // Apply monopoly bonus (double rent if no houses) - if property.development == 0 - && self.has_monopoly(property.owner, property.group_id, game_id) { - rent_amount *= 2; - } - - self.transfer_from(caller, property.owner, game_id, rent_amount); - - // Emit RentCollected event - world - .emit_event( - @RentCollected { - game_id, - property_id, - from_player: caller, - to_player: property.owner, - amount: rent_amount, - development_level: property.development, - timestamp: get_block_timestamp(), - }, - ); - - true - } - - fn get_property_value(ref self: ContractState, property_id: u8, game_id: u256) -> u256 { - let world = self.world_default(); - let property: Property = world.read_model((property_id, game_id)); - - if property.is_mortgaged { - // Mortgaged property value = property cost + development cost - mortgage debt - let mortgage_debt = property.cost_of_property / 2; - let interest = mortgage_debt * 10 / 100; - let total_debt = mortgage_debt + interest; - let development_value = property.development.into() * property.cost_of_house; - - if property.cost_of_property + development_value > total_debt { - property.cost_of_property + development_value - total_debt - } else { - 0 - } - } else { - // Unmortgaged property value = property cost + development cost - property.cost_of_property + (property.development.into() * property.cost_of_house) - } - } - - fn can_develop_evenly( - ref self: ContractState, - property_id: u8, - group_id: u8, - game_id: u256, - is_building: bool // true for building, false for selling - ) -> bool { - let world = self.world_default(); - let current_property: Property = world.read_model((property_id, game_id)); - let group_properties = self.get_properties_by_group(group_id, game_id); - - let mut i = 0; - loop { - if i >= group_properties.len() { - break true; - } - - let other_property_id = *group_properties.at(i); - if other_property_id != property_id { - let other_property: Property = world.read_model((other_property_id, game_id)); - - if is_building { - // When building: current property cannot have more houses than any other - // property in the group (must build evenly) - if current_property.development > other_property.development { - break false; - } - } else { - // When selling: current property cannot have fewer houses than any other - // property in the group after selling (current_development - 1) - if current_property.development < other_property.development { - break false; - } - } - } - - i += 1; - } - } - - fn can_develop_property(ref self: ContractState, property_id: u8, game_id: u256) -> bool { - let world = self.world_default(); - let property: Property = world.read_model((property_id, game_id)); - let caller = get_caller_address(); - - // Check basic requirements - if property.owner != caller || property.is_mortgaged || property.development >= 5 { - return false; - } - - // Check if player has monopoly - if !self.has_monopoly(caller, property.group_id, game_id) { - return false; - } - - // (must build evenly across all properties in group) - self.can_develop_evenly(property_id, property.group_id, game_id, true) - } - - fn can_sell_development(ref self: ContractState, property_id: u8, game_id: u256) -> bool { - let world = self.world_default(); - let property: Property = world.read_model((property_id, game_id)); - let caller = get_caller_address(); - - // Check basic requirements - if property.owner != caller || property.development == 0 { - return false; - } - - // Check even development rule for selling - // (must sell evenly across all properties in group) - self.can_develop_evenly(property_id, property.group_id, game_id, false) - } - - fn batch_generate_properties( - ref self: ContractState, - game_id: u256, - properties: Array<(u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8)>, - ) { - let mut world = self.world_default(); - let mut i = 0; - - loop { - if i >= properties.len() { - break; - } - - let ( - id, - name, - cost, - rent_site, - rent_1h, - rent_2h, - rent_3h, - rent_4h, - cost_house, - rent_hotel, - group_id, - ) = - *properties - .at(i); - - self - .generate_properties( - id, - game_id, - name, - cost, - rent_site, - rent_1h, - rent_2h, - rent_3h, - rent_4h, - cost_house, - rent_hotel, - false, - group_id, - ); - - i += 1; - }; - } - - fn create_new_game( - ref self: ContractState, - game_mode: GameMode, - player_symbol: PlayerSymbol, - number_of_players: u8, - ) -> u256 { - // Get default world - let mut world = self.world_default(); - - assert(number_of_players >= 2 && number_of_players <= 8, 'invalid no of players'); - - // Get the account address of the caller - let caller_address = get_caller_address(); - let caller_username = self.get_username_from_address(caller_address); - assert(caller_username != 0, 'PLAYER NOT REGISTERED'); - - let game_id = self.create_new_game_id(); - let timestamp = get_block_timestamp(); - - let player_hat = match player_symbol { - PlayerSymbol::Hat => caller_username, - _ => 0, - }; - - let player_car = match player_symbol { - PlayerSymbol::Car => caller_username, - _ => 0, - }; - let player_dog = match player_symbol { - PlayerSymbol::Dog => caller_username, - _ => 0, - }; - let player_thimble = match player_symbol { - PlayerSymbol::Thimble => caller_username, - _ => 0, - }; - let player_iron = match player_symbol { - PlayerSymbol::Iron => caller_username, - _ => 0, - }; - let player_battleship = match player_symbol { - PlayerSymbol::Battleship => caller_username, - _ => 0, - }; - let player_boot = match player_symbol { - PlayerSymbol::Boot => caller_username, - _ => 0, - }; - let player_wheelbarrow = match player_symbol { - PlayerSymbol::Wheelbarrow => caller_username, - _ => 0, - }; - - // Create a new game - let mut new_game: Game = GameTrait::new( - game_id, - caller_username, - game_mode, - player_hat, - player_car, - player_dog, - player_thimble, - player_iron, - player_battleship, - player_boot, - player_wheelbarrow, - number_of_players, - ); - - // If it's a multiplayer game, set status to Pending, - // else mark it as Ongoing (for single-player). - if game_mode == GameMode::MultiPlayer { - new_game.status = GameStatus::Pending; - } else { - new_game.status = GameStatus::Ongoing; - } - - world.write_model(@new_game); - - world.emit_event(@GameCreated { game_id, timestamp }); - - game_id - } - - - fn retrieve_player(ref self: ContractState, addr: ContractAddress) -> Player { - // Get default world - let mut world = self.world_default(); - let player: Player = world.read_model(addr); - - player - } - - fn retrieve_game(ref self: ContractState, game_id: u256) -> Game { - // Get default world - let mut world = self.world_default(); - //get the game state - let game: Game = world.read_model(game_id); - game - } - - fn generate_properties( - ref self: ContractState, - id: u8, - game_id: u256, - name: felt252, - cost_of_property: u256, - rent_site_only: u256, - rent_one_house: u256, - rent_two_houses: u256, - rent_three_houses: u256, - rent_four_houses: u256, - cost_of_house: u256, - rent_hotel: u256, - is_mortgaged: bool, - group_id: u8, - ) { - let mut world = self.world_default(); - let mut property: Property = world.read_model((id, game_id)); - - property = - PropertyTrait::new( - id, - game_id, - name, - cost_of_property, - rent_site_only, - rent_one_house, - rent_two_houses, - rent_three_houses, - rent_four_houses, - cost_of_house, - rent_hotel, - group_id, - ); - - let property_to_id: PropertyToId = PropertyToId { name, id }; - let id_to_property: IdToProperty = IdToProperty { id, name }; - - world.write_model(@property); - world.write_model(@property_to_id); - world.write_model(@id_to_property); - } - - fn get_property(ref self: ContractState, id: u8, game_id: u256) -> Property { - let mut world = self.world_default(); - let property = world.read_model((id, game_id)); - property - } - fn sell_property(ref self: ContractState, property_id: u8, game_id: u256) -> bool { - let mut world = self.world_default(); - let caller = get_caller_address(); - let mut property: Property = world.read_model((property_id, game_id)); - - assert(property.owner == caller, 'Can only sell your property'); - - property.for_sale = true; - world.write_model(@property); - - true - } - - fn buy_property(ref self: ContractState, property_id: u8, game_id: u256) -> bool { - let mut world = self.world_default(); - let caller = get_caller_address(); - let mut property: Property = world.read_model((property_id, game_id)); - let contract_address = get_contract_address(); - let zero_address: ContractAddress = contract_address_const::<0>(); - let amount: u256 = property.cost_of_property; - - // Validate property can be purchased - assert(property.id != 0, 'Property does not exist'); - - if property.owner == zero_address { - // Buying from bank - self.transfer_from(caller, contract_address, game_id, amount); - } else { - // Buying from another player - assert(property.owner != caller, 'Cannot buy your own property'); - assert(property.for_sale, 'Property is not for sale'); - self.transfer_from(caller, property.owner, game_id, amount); - } - - property.owner = caller; - property.for_sale = false; - - world.write_model(@property); - - // Emit property purchase event - let seller = if property.owner == zero_address { - get_contract_address() - } else { - property.owner - }; - - // Emit PropertyPurchased event - world - .emit_event( - @PropertyPurchased { - game_id, - property_id, - buyer: caller, - seller, - amount, - timestamp: get_block_timestamp(), - }, - ); - - true - } - fn mortgage_property(ref self: ContractState, property_id: u8, game_id: u256) -> bool { - let mut world = self.world_default(); - let caller = get_caller_address(); - let mut property: Property = world.read_model((property_id, game_id)); - - assert(property.owner == caller, 'Only the owner can mortgage '); - assert(property.is_mortgaged == false, 'Property is already mortgaged'); - - let amount: u256 = property.cost_of_property / 2; - let contract_address = get_contract_address(); - - self.transfer_from(contract_address, caller, game_id, amount); - - property.is_mortgaged = true; - world.write_model(@property); - - // Emit PropertyMortgaged event - world - .emit_event( - @PropertyMortgaged { - game_id, - property_id, - owner: caller, - amount_received: amount, - timestamp: get_block_timestamp(), - }, - ); - - true - } - - fn unmortgage_property(ref self: ContractState, property_id: u8, game_id: u256) -> bool { - let mut world = self.world_default(); - let caller = get_caller_address(); - let mut property: Property = world.read_model((property_id, game_id)); - - assert(property.owner == caller, 'Only the owner can unmortgage'); - assert(property.is_mortgaged, 'Property is not mortgaged'); - - let mortgage_amount: u256 = property.cost_of_property / 2; - let interest: u256 = mortgage_amount * 10 / 100; // 10% interest - let repay_amount: u256 = mortgage_amount + interest; - - self.transfer_from(caller, get_contract_address(), game_id, repay_amount); - - property.is_mortgaged = false; - world.write_model(@property); - - // Emit PropertyUnmortgaged event - world - .emit_event( - @PropertyUnmortgaged { - game_id, - property_id, - owner: caller, - amount_paid: repay_amount, - timestamp: get_block_timestamp(), - }, - ); - - true - } - - fn collect_rent(ref self: ContractState, property_id: u8, game_id: u256) -> bool { - let mut world = self.world_default(); - let caller = get_caller_address(); - let property: Property = world.read_model((property_id, game_id)); - let zero_address: ContractAddress = contract_address_const::<0>(); - - assert(property.owner != zero_address, 'Property is unowned'); - assert(property.owner != caller, 'You cannot pay rent to yourself'); - assert(property.is_mortgaged == false, 'No rent on mortgaged properties'); - - let rent_amount: u256 = 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, - 5 => property.rent_hotel, - _ => panic!("Invalid development level"), - }; - - self.transfer_from(caller, property.owner, game_id, rent_amount); - - // Emit RentCollected event - world - .emit_event( - @RentCollected { - game_id, - property_id, - from_player: caller, - to_player: property.owner, - amount: rent_amount, - development_level: property.development, - timestamp: get_block_timestamp(), - }, - ); - - true - } - - - fn transfer_from( - ref self: ContractState, - from: ContractAddress, - to: ContractAddress, - game_id: u256, - amount: u256, - ) { - let mut world = self.world_default(); - - let mut sender: GameBalance = world.read_model((from, game_id)); - let mut recepient: GameBalance = world.read_model((to, game_id)); - assert(sender.balance >= amount, 'insufficient funds'); - sender.balance -= amount; - recepient.balance += amount; - world.write_model(@sender); - world.write_model(@recepient); - } - - fn buy_house_or_hotel(ref self: ContractState, property_id: u8, game_id: u256) -> bool { - let mut world = self.world_default(); - let caller = get_caller_address(); - let mut property: Property = world.read_model((property_id, game_id)); - let contract_address = get_contract_address(); - - assert(self.can_develop_property(property_id, game_id), 'Cannot develop property'); - - let cost: u256 = property.cost_of_house; - self.transfer_from(caller, contract_address, game_id, cost); - - property.development += 1; // Increases to 5 (hotel) max - - world.write_model(@property); - - // Emit PropertyDeveloped event - world - .emit_event( - @PropertyDeveloped { - game_id, - property_id, - owner: caller, - development_level: property.development, - cost, - timestamp: get_block_timestamp(), - }, - ); - - true - } - - fn sell_house_or_hotel(ref self: ContractState, property_id: u8, game_id: u256) -> bool { - let mut world = self.world_default(); - let caller = get_caller_address(); - let mut property: Property = world.read_model((property_id, game_id)); - let contract_address = get_contract_address(); - - assert(self.can_sell_development(property_id, game_id), 'Cannot sell development'); - - let refund: u256 = property.cost_of_house / 2; - - self.transfer_from(contract_address, caller, game_id, refund); - - property.development -= 1; - - world.write_model(@property); - - // Emit PropertyDeveloped event (development level decreased) - world - .emit_event( - @PropertyDeveloped { - game_id, - property_id, - owner: caller, - development_level: property.development, - cost: refund, // negative cost (refund) - timestamp: get_block_timestamp(), - }, - ); - - true - } - - fn mint(ref self: ContractState, recepient: ContractAddress, game_id: u256, amount: u256) { - let mut world = self.world_default(); - - let mut receiver: GameBalance = world.read_model((recepient, game_id)); - let balance = receiver.balance + amount; - receiver.balance = balance; - world.write_model(@receiver); - } - } - - #[generate_trait] - impl InternalImpl of InternalTrait { - /// Use the default namespace "dojo_starter". This function is handy since the ByteArray - /// can't be const. - fn world_default(self: @ContractState) -> dojo::world::WorldStorage { - self.world(@"blockopoly") - } - } -} diff --git a/src/tests/test_player_model.cairo b/src/tests/test_player_model.cairo deleted file mode 100644 index cd8d224..0000000 --- a/src/tests/test_player_model.cairo +++ /dev/null @@ -1,504 +0,0 @@ -use dojo_starter::model::player_model::{ - Player, PlayerTrait, PlayerImpl, PlayerSymbol, PlayerSymbolTrait, PlayerSymbolImpl, - PlayerValidation, PlayerValidationImpl, PlayerActions, PlayerActionsImpl, zero_address, -}; - -use starknet::{ContractAddress, contract_address_const}; - - -fn create_test_player() -> Player { - PlayerImpl::new( - 'TestPlayer', contract_address_const::<0x123>(), false, PlayerSymbol::Hat, 1500_u256, - ) -} - -fn create_test_bot() -> Player { - PlayerImpl::new( - 'TestBot', contract_address_const::<0x456>(), true, PlayerSymbol::Car, 1500_u256, - ) -} - -#[test] -fn test_player_creation() { - let player = create_test_player(); - - assert(player.username == 'TestPlayer', 'Wrong username'); - assert(player.player == contract_address_const::<0x123>(), 'Wrong address'); - assert(!player.is_bot, 'Should not be bot'); - assert(player.balance == 1500_u256, 'Wrong initial balance'); - assert(player.position == 0, 'Wrong initial position'); - assert(!player.jailed, 'Should not be jailed'); - assert(player.player_symbol == PlayerSymbol::Hat, 'Wrong symbol'); - assert(player.total_games_played == 0, 'Wrong games played'); - assert(player.total_games_completed == 0, 'Wrong games completed'); - assert(player.total_games_won == 0, 'Wrong games won'); - assert(player.new_owner == zero_address(), 'Wrong new owner'); -} - -#[test] -fn test_bot_creation() { - let bot = create_test_bot(); - - assert(bot.is_bot, 'Should be bot'); - assert(bot.player_symbol == PlayerSymbol::Car, 'Wrong bot symbol'); -} - -#[test] -fn test_move_player_normal() { - let mut player = create_test_player(); - let initial_balance = player.balance; - - player.move_player(5, 40); - - assert(player.position == 5, 'Wrong position after move'); - assert(player.balance == initial_balance, 'Balance should not change'); -} - -#[test] -fn test_move_player_pass_go() { - let mut player = create_test_player(); - player.position = 38; - let initial_balance = player.balance; - - player.move_player(5, 40); - - assert(player.position == 3, 'Wrong position after passing go'); - assert(player.balance == initial_balance + 200, 'Should receive go money'); -} - -#[test] -fn test_move_player_multiple_laps() { - let mut player = create_test_player(); - let initial_balance = player.balance; - - player.move_player(85, 40); - - assert(player.position == 5, 'Wrong position'); - assert(player.balance == initial_balance + 400, 'Should receive money for 2 laps'); -} - -#[test] -#[should_panic(expected: ('Board not greater than 0',))] -fn test_move_player_zero_board_size() { - let mut player = create_test_player(); - player.move_player(5, 0); -} - -#[test] -fn test_pay_rent_successful() { - let mut payer = create_test_player(); - let mut recipient = create_test_bot(); - let rent_amount = 100_u256; - let payer_initial = payer.balance; - let recipient_initial = recipient.balance; - - let result = payer.pay_rent_to(ref recipient, rent_amount); - - assert(result, 'Rent payment should succeed'); - assert(payer.balance == payer_initial - rent_amount, 'Payer balance wrong'); - assert(recipient.balance == recipient_initial + rent_amount, 'Recipient balance wrong'); -} - -#[test] -fn test_pay_rent_insufficient_funds() { - let mut payer = create_test_player(); - let mut recipient = create_test_bot(); - let rent_amount = 2000_u256; - - let result = payer.pay_rent_to(ref recipient, rent_amount); - - assert(!result, 'Rent payment should fail'); -} - -#[test] -fn test_pay_rent_to_self() { - let mut player = create_test_player(); - let mut player_copy = create_test_player(); - - let result = player.pay_rent_to(ref player_copy, 100_u256); - - assert(!result, 'Should not pay rent to self'); -} - -#[test] -fn test_pay_rent_zero_amount() { - let mut payer = create_test_player(); - let mut recipient = create_test_bot(); - - let result = payer.pay_rent_to(ref recipient, 0_u256); - - assert(!result, 'Should not pay zero rent'); -} - -#[test] -fn test_buy_property_successful() { - let mut buyer = create_test_player(); - let mut seller = create_test_bot(); - let price = 200_u256; - let buyer_initial = buyer.balance; - let seller_initial = seller.balance; - - let result = buyer.buy_property(ref seller, price); - - assert(result, 'Should succeed'); - assert(buyer.balance == buyer_initial - price, 'Buyer balance wrong'); - assert(seller.balance == seller_initial + price, 'Seller balance wrong'); -} - -#[test] -fn test_buy_property_insufficient_funds() { - let mut buyer = create_test_player(); - let mut seller = create_test_bot(); - let price = 2000_u256; - - let result = buyer.buy_property(ref seller, price); - - assert(!result, 'Property purchase should fail'); -} - -#[test] -fn test_update_stats() { - let mut player = create_test_player(); - - player.update_stats(1, 1, 0); - assert(player.total_games_played == 1, 'Wrong games played'); - assert(player.total_games_completed == 1, 'Wrong games completed'); - assert(player.total_games_won == 0, 'Wrong games won'); - - player.update_stats(2, 1, 1); - assert(player.total_games_played == 3, 'Wrong cumulative games played'); - assert(player.total_games_completed == 2, 'Wrong cumulative games'); - assert(player.total_games_won == 1, 'Wrong cumulative games won'); -} - -#[test] -fn test_jail_status() { - let mut player = create_test_player(); - - player.set_jail_status(true); - assert(player.jailed, 'Should be jailed'); - assert(player.position == 10, 'Should be at jail position'); - - player.set_jail_status(false); - assert(!player.jailed, 'Should not be jailed'); - assert(player.position == 10, 'Position should remain at jail'); -} - -#[test] -fn test_transfer_ownership() { - let mut player = create_test_player(); - let new_owner = contract_address_const::<0x789>(); - - player.transfer_ownership(new_owner); - assert(player.new_owner == new_owner, 'Wrong new owner'); -} - -#[test] -fn test_collect_go_money() { - let mut player = create_test_player(); - let initial_balance = player.balance; - let go_amount = 200_u256; - - player.collect_go_money(go_amount); - assert(player.balance == initial_balance + go_amount, 'Wrong balance after go'); -} - -#[test] -fn test_pay_fee_successful() { - let mut player = create_test_player(); - let initial_balance = player.balance; - let fee = 50_u256; - - let result = player.pay_fee(fee); - - assert(result, 'Fee payment should succeed'); - assert(player.balance == initial_balance - fee, 'Wrong balance after fee'); -} - -#[test] -fn test_pay_fee_insufficient_funds() { - let mut player = create_test_player(); - let fee = 2000_u256; - - let result = player.pay_fee(fee); - - assert(!result, 'Fee payment should fail'); -} - -#[test] -fn test_can_afford() { - let player = create_test_player(); - - assert(player.can_afford(1500_u256), 'Should afford exact balance'); - assert(player.can_afford(1000_u256), 'Should afford less than balance'); - assert(!player.can_afford(2000_u256), 'Should not afford'); -} - -#[test] -fn test_is_bankrupt() { - let mut player = create_test_player(); - - assert(!player.is_bankrupt(), 'Should not be bankrupt'); - - player.balance = 0; - assert(player.is_bankrupt(), 'Should be bankrupt'); -} - -#[test] -fn test_get_win_rate() { - let mut player = create_test_player(); - - assert(player.get_win_rate() == 0, 'Win rate should be 0'); - - player.total_games_completed = 5; - player.total_games_won = 2; - assert(player.get_win_rate() == 40, 'Win rate should be 40%'); - - player.total_games_completed = 3; - player.total_games_won = 3; - assert(player.get_win_rate() == 100, 'Win rate should be 100%'); -} - -#[test] -fn test_player_symbol_is_valid() { - assert(PlayerSymbolImpl::is_valid(PlayerSymbol::Hat), 'Hat should be valid'); - assert(PlayerSymbolImpl::is_valid(PlayerSymbol::Car), 'Car should be valid'); - assert(PlayerSymbolImpl::is_valid(PlayerSymbol::Dog), 'Dog should be valid'); - assert(PlayerSymbolImpl::is_valid(PlayerSymbol::Thimble), 'Thimble should be valid'); - assert(PlayerSymbolImpl::is_valid(PlayerSymbol::Iron), 'Iron should be valid'); - assert(PlayerSymbolImpl::is_valid(PlayerSymbol::Battleship), 'Battleship should be valid'); - assert(PlayerSymbolImpl::is_valid(PlayerSymbol::Boot), 'Boot should be valid'); - assert(PlayerSymbolImpl::is_valid(PlayerSymbol::Wheelbarrow), 'Wheelbarrow should be valid'); -} - -#[test] -fn test_player_symbol_get_name() { - assert(PlayerSymbolImpl::get_name(PlayerSymbol::Hat) == 'Hat', 'Wrong hat name'); - assert(PlayerSymbolImpl::get_name(PlayerSymbol::Car) == 'Car', 'Wrong car name'); - assert(PlayerSymbolImpl::get_name(PlayerSymbol::Dog) == 'Dog', 'Wrong dog name'); - assert(PlayerSymbolImpl::get_name(PlayerSymbol::Thimble) == 'Thimble', 'Wrong thimble name'); - assert(PlayerSymbolImpl::get_name(PlayerSymbol::Iron) == 'Iron', 'Wrong iron name'); - assert( - PlayerSymbolImpl::get_name(PlayerSymbol::Battleship) == 'Battleship', - 'Wrong battleship name', - ); - assert(PlayerSymbolImpl::get_name(PlayerSymbol::Boot) == 'Boot', 'Wrong boot name'); - assert( - PlayerSymbolImpl::get_name(PlayerSymbol::Wheelbarrow) == 'Wheelbarrow', - 'Wrong wheelbarrow name', - ); -} - -#[test] -fn test_player_symbol_from_felt() { - assert( - PlayerSymbolImpl::from_felt('Hat') == Option::Some(PlayerSymbol::Hat), - 'Wrong hat conversion', - ); - assert( - PlayerSymbolImpl::from_felt('Car') == Option::Some(PlayerSymbol::Car), - 'Wrong car conversion', - ); - assert( - PlayerSymbolImpl::from_felt('Dog') == Option::Some(PlayerSymbol::Dog), - 'Wrong dog conversion', - ); - assert( - PlayerSymbolImpl::from_felt('Invalid') == Option::None, 'Should return None for invalid', - ); -} - -#[test] -fn test_validate_creation_success() { - let result = PlayerValidationImpl::validate_creation( - 'TestPlayer', contract_address_const::<0x123>(), PlayerSymbol::Hat, 1500_u256, - ); - assert(result, 'Valid creation should pass'); -} - -#[test] -fn test_validate_creation_zero_address() { - let result = PlayerValidationImpl::validate_creation( - 'TestPlayer', zero_address(), PlayerSymbol::Hat, 1500_u256, - ); - assert(!result, 'Should fail validation'); -} - -#[test] -fn test_validate_creation_empty_username() { - let result = PlayerValidationImpl::validate_creation( - 0, contract_address_const::<0x123>(), PlayerSymbol::Hat, 1500_u256, - ); - assert(!result, 'Should fail validation'); -} - -#[test] -fn test_validate_transaction_success() { - let payer = create_test_player(); - let recipient = create_test_bot(); - - let result = PlayerValidationImpl::validate_transaction(@payer, @recipient, 100_u256); - assert(result, 'Valid transaction should pass'); -} - -#[test] -fn test_validate_transaction_insufficient_funds() { - let payer = create_test_player(); - let recipient = create_test_bot(); - - let result = PlayerValidationImpl::validate_transaction(@payer, @recipient, 2000_u256); - assert(!result, 'Should fail validation'); -} - -#[test] -fn test_validate_transaction_same_player() { - let player = create_test_player(); - - let result = PlayerValidationImpl::validate_transaction(@player, @player, 100_u256); - assert(!result, 'Should fail validation'); -} - -#[test] -fn test_validate_transaction_zero_amount() { - let payer = create_test_player(); - let recipient = create_test_bot(); - - let result = PlayerValidationImpl::validate_transaction(@payer, @recipient, 0_u256); - assert(!result, 'Should fail validation'); -} - -#[test] -fn test_validate_username() { - assert(PlayerValidationImpl::validate_username('ValidName'), 'Valid username should pass'); - assert(!PlayerValidationImpl::validate_username(0), 'Zero username should fail'); -} - -// PlayerActions Tests -#[test] -fn test_go_to_jail() { - let mut player = create_test_player(); - - player.go_to_jail(); - - assert(player.jailed, 'Should be jailed'); - assert(player.position == 10, 'Should be at jail position'); -} - -#[test] -fn test_get_out_of_jail_with_payment() { - let mut player = create_test_player(); - player.jailed = true; - let initial_balance = player.balance; - let fee = 50_u256; - - let result = player.get_out_of_jail(true, fee); - - assert(result, 'Should get out of jail'); - assert(!player.jailed, 'Should not be jailed'); - assert(player.balance == initial_balance - fee, 'Wrong balance after fee'); -} - -#[test] -fn test_get_out_of_jail_without_payment() { - let mut player = create_test_player(); - player.jailed = true; - let initial_balance = player.balance; - - let result = player.get_out_of_jail(false, 0); - - assert(result, 'Should get out of jail'); - assert(!player.jailed, 'Should not be jailed'); - assert(player.balance == initial_balance, 'Balance should not change'); -} - -#[test] -fn test_get_out_of_jail_insufficient_funds() { - let mut player = create_test_player(); - player.jailed = true; - let fee = 2000_u256; - - let result = player.get_out_of_jail(true, fee); - - assert(!result, 'Should fail to get out of jail'); - assert(player.jailed, 'Should still be jailed'); -} - -#[test] -fn test_pass_go() { - let mut player = create_test_player(); - let initial_balance = player.balance; - let go_amount = 200_u256; - - player.pass_go(go_amount); - - assert(player.balance == initial_balance + go_amount, 'Wrong balance after passing go'); -} - -#[test] -fn test_pay_tax_successful() { - let mut player = create_test_player(); - let initial_balance = player.balance; - let tax = 100_u256; - - let result = player.pay_tax(tax); - - assert(result, 'Tax payment should succeed'); - assert(player.balance == initial_balance - tax, 'Wrong balance after tax'); -} - -#[test] -fn test_pay_tax_insufficient_funds() { - let mut player = create_test_player(); - let tax = 2000_u256; - - let result = player.pay_tax(tax); - - assert(!result, 'Tax payment should fail'); -} - -#[test] -fn test_receive_money() { - let mut player = create_test_player(); - let initial_balance = player.balance; - let amount = 300_u256; - - player.receive_money(amount); - - assert(player.balance == initial_balance + amount, 'Wrong balance'); -} - -#[test] -fn test_reset_position() { - let mut player = create_test_player(); - player.position = 25; - player.jailed = true; - - player.reset_position(); - - assert(player.position == 0, 'Position should be reset to 0'); - assert(!player.jailed, 'Should not be jailed'); -} - - -#[test] -fn test_multiple_operations() { - let mut player1 = create_test_player(); - let mut player2 = create_test_bot(); - - player1.move_player(42, 40); - assert(player1.position == 2, 'Wrong position after move'); - assert(player1.balance == 1700, 'Wrong balance after passing go'); - - let rent_paid = player1.pay_rent_to(ref player2, 150_u256); - assert(rent_paid, 'Rent payment should succeed'); - assert(player1.balance == 1550, 'Wrong balance after rent'); - assert(player2.balance == 1650, 'Wrong recipient balance'); - - player1.go_to_jail(); - assert(player1.jailed, 'Should be jailed'); - assert(player1.position == 10, 'Should be at jail'); - - let jail_exit = player1.get_out_of_jail(true, 50_u256); - assert(jail_exit, 'Should get out of jail'); - assert(player1.balance == 1500, 'Wrong balance after jail fee'); -} diff --git a/src/tests/test_property_enhanced.cairo b/src/tests/test_property_enhanced.cairo deleted file mode 100644 index ddccaf7..0000000 --- a/src/tests/test_property_enhanced.cairo +++ /dev/null @@ -1,372 +0,0 @@ -#[cfg(test)] -mod tests { - use dojo_cairo_test::WorldStorageTestTrait; - use dojo::model::{ModelStorage, ModelStorageTest}; - use dojo::world::WorldStorageTrait; - use dojo_cairo_test::{ - spawn_test_world, NamespaceDef, TestResource, ContractDefTrait, ContractDef, - }; - - use dojo_starter::systems::world::{world}; - use dojo_starter::interfaces::IWorld::{IWorldDispatcher, IWorldDispatcherTrait}; - use dojo_starter::model::game_model::{ - Game, m_Game, GameMode, GameStatus, GameCounter, m_GameCounter, GameBalance, m_GameBalance, - }; - use dojo_starter::model::property_model::{ - Property, m_Property, IdToProperty, m_IdToProperty, PropertyToId, m_PropertyToId, - }; - use dojo_starter::model::player_model::{ - Player, m_Player, UsernameToAddress, m_UsernameToAddress, AddressToUsername, - m_AddressToUsername, PlayerSymbol, - }; - use starknet::{testing, ContractAddress, get_caller_address, contract_address_const}; - - fn namespace_def() -> NamespaceDef { - let ndef = NamespaceDef { - namespace: "blockopoly", - resources: [ - TestResource::Model(m_Player::TEST_CLASS_HASH), - TestResource::Model(m_Game::TEST_CLASS_HASH), - TestResource::Model(m_GameBalance::TEST_CLASS_HASH), - TestResource::Model(m_Property::TEST_CLASS_HASH), - TestResource::Model(m_IdToProperty::TEST_CLASS_HASH), - TestResource::Model(m_PropertyToId::TEST_CLASS_HASH), - TestResource::Model(m_UsernameToAddress::TEST_CLASS_HASH), - TestResource::Model(m_AddressToUsername::TEST_CLASS_HASH), - TestResource::Model(m_GameCounter::TEST_CLASS_HASH), - TestResource::Event(world::e_PlayerCreated::TEST_CLASS_HASH), - TestResource::Event(world::e_GameCreated::TEST_CLASS_HASH), - TestResource::Event(world::e_PlayerJoined::TEST_CLASS_HASH), - TestResource::Event(world::e_GameStarted::TEST_CLASS_HASH), - TestResource::Event(world::e_PropertyPurchased::TEST_CLASS_HASH), - TestResource::Event(world::e_PropertyMortgaged::TEST_CLASS_HASH), - TestResource::Event(world::e_PropertyUnmortgaged::TEST_CLASS_HASH), - TestResource::Event(world::e_RentCollected::TEST_CLASS_HASH), - TestResource::Event(world::e_PropertyDeveloped::TEST_CLASS_HASH), - TestResource::Contract(world::TEST_CLASS_HASH), - ] - .span(), - }; - - ndef - } - - fn contract_defs() -> Span { - [ - ContractDefTrait::new(@"blockopoly", @"world") - .with_writer_of([dojo::utils::bytearray_hash(@"blockopoly")].span()) - ] - .span() - } - - fn setup_game_with_player() -> (IWorldDispatcher, u256, ContractAddress) { - let caller = contract_address_const::<'player1'>(); - let username = 'Player1'; - - let ndef = namespace_def(); - let mut world = spawn_test_world([ndef].span()); - world.sync_perms_and_inits(contract_defs()); - - let (contract_address, _) = world.dns(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; - - testing::set_contract_address(caller); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); - - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - - // Mint initial balance - actions_system.mint(caller, game_id, 10000); - - (actions_system, game_id, caller) - } - - #[test] - fn test_get_properties_owned_by_player() { - let (actions_system, game_id, caller) = setup_game_with_player(); - - // Generate test properties - actions_system - .generate_properties( - 1, game_id, 'Property1', 200, 10, 100, 200, 300, 400, 300, 500, false, 1, - ); - actions_system - .generate_properties( - 2, game_id, 'Property2', 250, 15, 120, 220, 320, 420, 350, 550, false, 1, - ); - actions_system - .generate_properties( - 3, game_id, 'Property3', 300, 20, 140, 240, 340, 440, 400, 600, false, 2, - ); - - // Buy properties 1 and 2 - testing::set_contract_address(caller.clone()); - actions_system.buy_property(1, game_id); - actions_system.buy_property(2, game_id); - - // Check owned properties - let owned_properties = actions_system.get_properties_owned_by_player(caller, game_id); - assert(owned_properties.len() == 2, 'Should own 2 properties'); - assert(*owned_properties.at(0) == 1, 'Should own property 1'); - assert(*owned_properties.at(1) == 2, 'Should own property 2'); - } - - #[test] - fn test_get_properties_by_group() { - let (actions_system, game_id, _caller) = setup_game_with_player(); - - // Generate properties in group 1 - actions_system - .generate_properties( - 1, game_id, 'Property1', 200, 10, 100, 200, 300, 400, 300, 500, false, 1, - ); - actions_system - .generate_properties( - 2, game_id, 'Property2', 250, 15, 120, 220, 320, 420, 350, 550, false, 1, - ); - actions_system - .generate_properties( - 3, game_id, 'Property3', 300, 20, 140, 240, 340, 440, 400, 600, false, 2, - ); - - let group_1_properties = actions_system.get_properties_by_group(1, game_id); - assert(group_1_properties.len() == 2, 'Group 1 shld have 2 properties'); - assert(*group_1_properties.at(0) == 1, 'Group 1 shld contain property 1'); - assert(*group_1_properties.at(1) == 2, 'Group 1 shld contain property 2'); - - let group_2_properties = actions_system.get_properties_by_group(2, game_id); - assert(group_2_properties.len() == 1, 'Group 2 shld have 1 property'); - assert(*group_2_properties.at(0) == 3, 'Group 2 shld contain property 3'); - } - - #[test] - fn test_monopoly_detection() { - let (actions_system, game_id, caller) = setup_game_with_player(); - - // Generate properties in group 1 - actions_system - .generate_properties( - 1, game_id, 'Property1', 200, 10, 100, 200, 300, 400, 300, 500, false, 1, - ); - actions_system - .generate_properties( - 2, game_id, 'Property2', 250, 15, 120, 220, 320, 420, 350, 550, false, 1, - ); - - // Player doesn't have monopoly initially - assert(!actions_system.has_monopoly(caller, 1, game_id), 'Should not have monopoly'); - - // Buy first property - testing::set_contract_address(caller); - actions_system.buy_property(1, game_id); - assert(!actions_system.has_monopoly(caller, 1, game_id), 'Shld nt have monopoly w/1 ppty'); - - // Buy second property to complete monopoly - actions_system.buy_property(2, game_id); - assert(actions_system.has_monopoly(caller, 1, game_id), 'Should have monopoly'); - } - - #[test] - fn test_monopoly_rent_bonus() { - let (actions_system, game_id, caller) = setup_game_with_player(); - let player2 = contract_address_const::<'player2'>(); - - // Register second player - testing::set_contract_address(player2); - actions_system.register_new_player('Player2', false, PlayerSymbol::Car, 100); - actions_system.mint(player2, game_id, 5000); - - // Generate properties in group 1 - actions_system - .generate_properties( - 1, game_id, 'Property1', 200, 10, 100, 200, 300, 400, 300, 500, false, 1, - ); - actions_system - .generate_properties( - 2, game_id, 'Property2', 250, 15, 120, 220, 320, 420, 350, 550, false, 1, - ); - - // Player 1 buys both properties to create monopoly - testing::set_contract_address(caller); - actions_system.buy_property(1, game_id); - actions_system.buy_property(2, game_id); - - // Player 2 pays rent with monopoly bonus - testing::set_contract_address(player2); - let balance_before = actions_system.get_players_balance(player2, game_id); - actions_system.collect_rent_with_monopoly(1, game_id); - let balance_after = actions_system.get_players_balance(player2, game_id); - - // Should pay double rent (20 instead of 10) - assert(balance_before - balance_after == 20, 'Shld pay 2x rent for monopoly'); - } - - #[test] - fn test_property_value_calculation() { - let (actions_system, game_id, caller) = setup_game_with_player(); - - // Generate property - actions_system - .generate_properties( - 1, game_id, 'Property1', 200, 10, 100, 200, 300, 400, 300, 500, false, 1, - ); - - // Buy property - testing::set_contract_address(caller); - actions_system.buy_property(1, game_id); - - // Test value without development - let value = actions_system.get_property_value(1, game_id); - assert(value == 200, 'Basic ppty value shld be 200'); - - // Check property before development - let prop_before = actions_system.get_property(1, game_id); - assert(prop_before.development == 0, 'Development should be 0'); - assert(prop_before.cost_of_property == 200, 'Cost should be 200'); - assert(prop_before.cost_of_house == 300, 'House cost should be 300'); - - // Develop property - actions_system.buy_house_or_hotel(1, game_id); - - // Check property after development - let prop_after = actions_system.get_property(1, game_id); - assert(prop_after.development == 1, 'Development should be 1'); - - let value_with_house = actions_system.get_property_value(1, game_id); - - // Debug: Calculate expected value manually - let expected_value = prop_after.cost_of_property - + (prop_after.development.into() * prop_after.cost_of_house); - - // This should show us what we're actually getting vs expected - assert(value_with_house == expected_value, 'Value != manual calc'); - assert(value_with_house == 500, 'Ppty w/house shld be 500'); // 200 + 300 - - // Test mortgaged property value - actions_system.mortgage_property(1, game_id); - let mortgaged_value = actions_system.get_property_value(1, game_id); - assert(mortgaged_value < value_with_house, 'Mortgaged should be less'); - } - - #[test] - fn test_can_develop_property() { - let (actions_system, game_id, caller) = setup_game_with_player(); - - // Generate properties in group 1 - actions_system - .generate_properties( - 1, game_id, 'Property1', 200, 10, 100, 200, 300, 400, 300, 500, false, 1, - ); - actions_system - .generate_properties( - 2, game_id, 'Property2', 250, 15, 120, 220, 320, 420, 350, 550, false, 1, - ); - - testing::set_contract_address(caller); - - // Cannot develop unowned property - assert(!actions_system.can_develop_property(1, game_id), 'Cannot develop unowned property'); - - // Buy one property - actions_system.buy_property(1, game_id); - assert(!actions_system.can_develop_property(1, game_id), 'Cannot develop without monopoly'); - - // Buy second property to complete monopoly - actions_system.buy_property(2, game_id); - assert(actions_system.can_develop_property(1, game_id), 'can develop monopoly'); - - // Mortgage property - actions_system.mortgage_property(1, game_id); - assert(!actions_system.can_develop_property(1, game_id), 'Cannot develop mortgaged ppty'); - } - - #[test] - fn test_batch_property_generation() { - let (actions_system, game_id, _) = setup_game_with_player(); - - let mut properties = ArrayTrait::new(); - properties.append((1, 'Prop1', 200, 10, 100, 200, 300, 400, 300, 500, 1)); - properties.append((2, 'Prop2', 250, 15, 120, 220, 320, 420, 350, 550, 1)); - properties.append((3, 'Prop3', 300, 20, 140, 240, 340, 440, 400, 600, 2)); - - actions_system.batch_generate_properties(game_id, properties); - - // Verify all properties were created - let prop1 = actions_system.get_property(1, game_id); - assert(prop1.name == 'Prop1', 'Property 1 name incorrect'); - assert(prop1.cost_of_property == 200, 'Property 1 cost incorrect'); - - let prop2 = actions_system.get_property(2, game_id); - assert(prop2.name == 'Prop2', 'Property 2 name incorrect'); - assert(prop2.cost_of_property == 250, 'Property 2 cost incorrect'); - - let prop3 = actions_system.get_property(3, game_id); - assert(prop3.name == 'Prop3', 'Property 3 name incorrect'); - assert(prop3.cost_of_property == 300, 'Property 3 cost incorrect'); - } - - #[test] - fn test_property_ownership_edge_cases() { - let (actions_system, game_id, caller) = setup_game_with_player(); - - // Generate property - actions_system - .generate_properties( - 1, game_id, 'Property1', 200, 10, 100, 200, 300, 400, 300, 500, false, 1, - ); - - testing::set_contract_address(caller); - - // Buy property - actions_system.buy_property(1, game_id); - - // Try to collect rent from own property (should fail) - // This test should panic, but we'll verify the property owner - let property = actions_system.get_property(1, game_id); - assert(property.owner == caller, 'Ppty shld be owned by caller'); - } - - #[test] - fn test_development_constraints() { - let (actions_system, game_id, caller) = setup_game_with_player(); - - // Generate properties in group 1 (need monopoly to develop) - actions_system - .generate_properties( - 1, game_id, 'Property1', 200, 10, 100, 200, 300, 400, 300, 500, false, 1, - ); - actions_system - .generate_properties( - 2, game_id, 'Property2', 250, 15, 120, 220, 320, 420, 350, 550, false, 1, - ); - - testing::set_contract_address(caller); - - // Buy both properties to get monopoly - actions_system.buy_property(1, game_id); - actions_system.buy_property(2, game_id); - - // Develop both properties evenly to maximum (5 levels) - // Level 1 for both - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(2, game_id); - // Level 2 for both - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(2, game_id); - // Level 3 for both - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(2, game_id); - // Level 4 for both - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(2, game_id); - // Level 5 (hotel) for both - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(2, game_id); - - let property = actions_system.get_property(1, game_id); - assert(property.development == 5, 'Should have maximum development'); - - // Verify cannot develop further - assert(!actions_system.can_develop_property(1, game_id), 'Cannot develop beyond maximum'); - } -} diff --git a/src/tests/test_world.cairo b/src/tests/test_world.cairo index e93f86e..222511c 100644 --- a/src/tests/test_world.cairo +++ b/src/tests/test_world.cairo @@ -7,22 +7,36 @@ mod tests { spawn_test_world, NamespaceDef, TestResource, ContractDefTrait, ContractDef, }; - use dojo_starter::systems::world::{world}; + use dojo_starter::systems::actions::{actions}; - use dojo_starter::interfaces::IWorld::{IWorldDispatcher, IWorldDispatcherTrait}; + use dojo_starter::interfaces::IActions::{IActionsDispatcher, IActionsDispatcherTrait}; use dojo_starter::model::game_model::{ - Game, m_Game, GameMode, GameStatus, GameCounter, m_GameCounter, GameBalance, m_GameBalance, + Game, m_Game, GameType, GameStatus, GameCounter, m_GameCounter, GameBalance, m_GameBalance, }; + use dojo_starter::model::player_model::{ + Player, m_Player, UsernameToAddress, m_UsernameToAddress, AddressToUsername, + m_AddressToUsername, IsRegistered, m_IsRegistered, + }; + + use dojo_starter::model::game_player_model::{GamePlayer, m_GamePlayer, PlayerSymbol}; + use dojo_starter::model::property_model::{ Property, m_Property, IdToProperty, m_IdToProperty, PropertyToId, m_PropertyToId, }; - use dojo_starter::model::player_model::{ - Player, m_Player, UsernameToAddress, m_UsernameToAddress, AddressToUsername, - m_AddressToUsername, PlayerSymbol, + use dojo_starter::model::utility_model::{ + Utility, m_Utility, IdToUtility, m_IdToUtility, UtilityToId, m_UtilityToId, + }; + use dojo_starter::model::rail_road_model::{ + RailRoad, m_RailRoad, IdToRailRoad, m_IdToRailRoad, RailRoadToId, m_RailRoadToId, }; + use dojo_starter::model::chance_model::{Chance, m_Chance}; + use dojo_starter::model::community_chest_model::{CommunityChest, m_CommunityChest}; + use dojo_starter::model::jail_model::{Jail, m_Jail}; + use dojo_starter::model::go_free_parking_model::{Go, m_Go}; + use dojo_starter::model::tax_model::{Tax, m_Tax}; use starknet::{testing, get_caller_address, contract_address_const}; fn namespace_def() -> NamespaceDef { @@ -30,19 +44,32 @@ mod tests { namespace: "blockopoly", resources: [ TestResource::Model(m_Player::TEST_CLASS_HASH), - TestResource::Model(m_Game::TEST_CLASS_HASH), - TestResource::Model(m_GameBalance::TEST_CLASS_HASH), TestResource::Model(m_Property::TEST_CLASS_HASH), TestResource::Model(m_IdToProperty::TEST_CLASS_HASH), TestResource::Model(m_PropertyToId::TEST_CLASS_HASH), + TestResource::Model(m_Game::TEST_CLASS_HASH), + TestResource::Model(m_GameBalance::TEST_CLASS_HASH), TestResource::Model(m_UsernameToAddress::TEST_CLASS_HASH), TestResource::Model(m_AddressToUsername::TEST_CLASS_HASH), + TestResource::Model(m_IsRegistered::TEST_CLASS_HASH), TestResource::Model(m_GameCounter::TEST_CLASS_HASH), - TestResource::Event(world::e_PlayerCreated::TEST_CLASS_HASH), - TestResource::Event(world::e_GameCreated::TEST_CLASS_HASH), - TestResource::Event(world::e_PlayerJoined::TEST_CLASS_HASH), - TestResource::Event(world::e_GameStarted::TEST_CLASS_HASH), - TestResource::Contract(world::TEST_CLASS_HASH), + TestResource::Model(m_Utility::TEST_CLASS_HASH), + TestResource::Model(m_IdToUtility::TEST_CLASS_HASH), + TestResource::Model(m_UtilityToId::TEST_CLASS_HASH), + TestResource::Model(m_RailRoad::TEST_CLASS_HASH), + TestResource::Model(m_IdToRailRoad::TEST_CLASS_HASH), + TestResource::Model(m_RailRoadToId::TEST_CLASS_HASH), + TestResource::Model(m_Chance::TEST_CLASS_HASH), + TestResource::Model(m_CommunityChest::TEST_CLASS_HASH), + TestResource::Model(m_Jail::TEST_CLASS_HASH), + TestResource::Model(m_Go::TEST_CLASS_HASH), + TestResource::Model(m_Tax::TEST_CLASS_HASH), + TestResource::Model(m_GamePlayer::TEST_CLASS_HASH), + TestResource::Event(actions::e_PlayerCreated::TEST_CLASS_HASH), + TestResource::Event(actions::e_GameCreated::TEST_CLASS_HASH), + TestResource::Event(actions::e_PlayerJoined::TEST_CLASS_HASH), + TestResource::Event(actions::e_GameStarted::TEST_CLASS_HASH), + TestResource::Contract(actions::TEST_CLASS_HASH), ] .span(), }; @@ -52,13 +79,30 @@ mod tests { fn contract_defs() -> Span { [ - ContractDefTrait::new(@"blockopoly", @"world") + ContractDefTrait::new(@"blockopoly", @"actions") .with_writer_of([dojo::utils::bytearray_hash(@"blockopoly")].span()) ] .span() } + #[test] + fn test_roll_dice() { + 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 }; + + let (dice_1, dice_2) = actions_system.roll_dice(); + + assert(dice_2 <= 6, 'incorrect roll'); + assert(dice_1 <= 6, 'incorrect roll'); + assert(dice_2 > 0, 'incorrect roll'); + assert(dice_1 > 0, 'incorrect roll'); + } + #[test] fn test_player_registration() { let caller_1 = contract_address_const::<'aji'>(); @@ -68,15 +112,15 @@ mod tests { let mut world = spawn_test_world([ndef].span()); world.sync_perms_and_inits(contract_defs()); - let (contract_address, _) = world.dns(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; testing::set_contract_address(caller_1); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); + actions_system.register_new_player(username); let player: Player = actions_system.retrieve_player(caller_1); - println!("username: {}", player.username); - assert(player.player == caller_1, 'incorrect address'); + + assert(player.address == caller_1, 'incorrect address'); assert(player.username == 'Aji', 'incorrect username'); } #[test] @@ -90,14 +134,14 @@ mod tests { let mut world = spawn_test_world([ndef].span()); world.sync_perms_and_inits(contract_defs()); - let (contract_address, _) = world.dns(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; testing::set_contract_address(caller_1); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); + actions_system.register_new_player(username); testing::set_contract_address(caller_2); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); + actions_system.register_new_player(username); } #[test] @@ -111,14 +155,14 @@ mod tests { let mut world = spawn_test_world([ndef].span()); world.sync_perms_and_inits(contract_defs()); - let (contract_address, _) = world.dns(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; testing::set_contract_address(caller_1); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); + actions_system.register_new_player(username); testing::set_contract_address(caller_1); - actions_system.register_new_player(username1, false, PlayerSymbol::Car, 100); + actions_system.register_new_player(username1); } #[test] #[should_panic] @@ -131,32 +175,16 @@ mod tests { let mut world = spawn_test_world([ndef].span()); world.sync_perms_and_inits(contract_defs()); - let (contract_address, _) = world.dns(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; testing::set_contract_address(caller_1); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); + actions_system.register_new_player(username); testing::set_contract_address(caller_1); - actions_system.register_new_player(username1, false, PlayerSymbol::Car, 100); + actions_system.register_new_player(username1); } - #[test] - #[should_panic] - fn test_player_registration_bot_tries_registering() { - let caller_1 = contract_address_const::<'aji'>(); - let username = 'Aji'; - - let ndef = namespace_def(); - let mut world = spawn_test_world([ndef].span()); - world.sync_perms_and_inits(contract_defs()); - - let (contract_address, _) = world.dns(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; - - testing::set_contract_address(caller_1); - actions_system.register_new_player(username, true, PlayerSymbol::Car, 100); - } #[test] fn test_create_game() { @@ -167,20 +195,18 @@ mod tests { let mut world = spawn_test_world([ndef].span()); world.sync_perms_and_inits(contract_defs()); - let (contract_address, _) = world.dns(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; testing::set_contract_address(caller_1); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); + actions_system.register_new_player(username); testing::set_contract_address(caller_1); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); + let game_id = actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); assert(game_id == 1, 'Wrong game id'); - println!("game_id: {}", game_id); let game: Game = actions_system.retrieve_game(game_id); assert(game.created_by == username, 'Wrong game id'); - println!("creator: {}", game.created_by); } #[test] @@ -193,19 +219,18 @@ mod tests { let mut world = spawn_test_world([ndef].span()); world.sync_perms_and_inits(contract_defs()); - let (contract_address, _) = world.dns(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; testing::set_contract_address(caller_1); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); + actions_system.register_new_player(username); testing::set_contract_address(caller_1); - let _game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); + let _game_id = actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); testing::set_contract_address(caller_1); - let game_id_1 = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); + let game_id_1 = actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); assert(game_id_1 == 2, 'Wrong game id'); - println!("game_id: {}", game_id_1); } #[test] @@ -217,489 +242,1922 @@ mod tests { let mut world = spawn_test_world([ndef].span()); world.sync_perms_and_inits(contract_defs()); - let (contract_address, _) = world.dns(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; testing::set_contract_address(caller_1); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); + let game_id = actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); assert(game_id == 1, 'Wrong game id'); - println!("game_id: {}", game_id); } + #[test] - fn test_property() { + fn test_join_game() { let caller_1 = contract_address_const::<'aji'>(); + let caller_2 = contract_address_const::<'john'>(); + let username = 'Ajidokwu'; + let username_1 = 'John'; let ndef = namespace_def(); let mut world = spawn_test_world([ndef].span()); world.sync_perms_and_inits(contract_defs()); - let (contract_address, _) = world.dns(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; - actions_system - .generate_properties(1, 1, 'Eth_Lane', 200, 10, 100, 200, 300, 400, 300, 500, false, 4); + 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); - let property = actions_system.get_property(1, 1); + testing::set_contract_address(caller_1); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); - assert(property.id == 1, 'wrong id'); + testing::set_contract_address(caller_2); + actions_system.join_game(PlayerSymbol::Dog, 1); } #[test] - fn test_buy_property() { + #[should_panic] + fn test_join_game_with_same_symbol_as_creator() { let caller_1 = contract_address_const::<'aji'>(); + let caller_2 = contract_address_const::<'john'>(); let username = 'Ajidokwu'; + let username_1 = 'John'; let ndef = namespace_def(); let mut world = spawn_test_world([ndef].span()); world.sync_perms_and_inits(contract_defs()); - let (contract_address, _) = world.dns(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; - - actions_system - .generate_properties(1, 1, 'Eth_Lane', 200, 10, 100, 200, 300, 400, 300, 500, false, 4); - - let property = actions_system.get_property(1, 1); + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; - assert(property.id == 1, 'wrong id'); - - testing::set_contract_address(caller_1); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); + testing::set_contract_address(caller_2); + actions_system.register_new_player(username_1); testing::set_contract_address(caller_1); - let game_id: u256 = actions_system - .create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); - - actions_system.mint(caller_1, game_id, 10000); + actions_system.register_new_player(username); testing::set_contract_address(caller_1); - actions_system.buy_property(1, game_id); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); - let game: Game = actions_system.retrieve_game(game_id); - assert(game.created_by == username, 'Wrong game id'); - let property = actions_system.get_property(1, game_id); - assert(property.owner == caller_1, 'invalid property txn'); + testing::set_contract_address(caller_2); + actions_system.join_game(PlayerSymbol::Hat, 1); } - #[test] - fn test_mint_and_balance() { + #[should_panic] + fn test_join_yet_to_be_created_game_() { let caller_1 = contract_address_const::<'aji'>(); + let caller_2 = contract_address_const::<'john'>(); let username = 'Ajidokwu'; + let username_1 = 'John'; let ndef = namespace_def(); let mut world = spawn_test_world([ndef].span()); world.sync_perms_and_inits(contract_defs()); - let (contract_address, _) = world.dns(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; - testing::set_contract_address(caller_1); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); + testing::set_contract_address(caller_2); + actions_system.register_new_player(username_1); testing::set_contract_address(caller_1); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); - - actions_system.mint(caller_1, game_id, 10000); + actions_system.register_new_player(username); - let player_balance = actions_system.get_players_balance(caller_1, game_id); - assert(player_balance == 10000, 'mint failure'); + testing::set_contract_address(caller_2); + actions_system.join_game(PlayerSymbol::Hat, 1); } + #[test] - fn test_buy_property_from_a_player() { + fn test_each_player_gets_starting_balance() { let caller_1 = contract_address_const::<'aji'>(); - let caller_2 = contract_address_const::<'ajidokwu'>(); + 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(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; - testing::set_contract_address(caller_1); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); + testing::set_contract_address(caller_2); + actions_system.register_new_player(username_1); testing::set_contract_address(caller_1); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); + actions_system.register_new_player(username); + + testing::set_contract_address(caller_3); + actions_system.register_new_player(username_2); - actions_system.mint(caller_1, game_id, 10000); - actions_system.mint(caller_2, game_id, 10000); + testing::set_contract_address(caller_4); + actions_system.register_new_player(username_3); testing::set_contract_address(caller_1); - actions_system.buy_property(1, game_id); - actions_system.sell_property(1, game_id); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); testing::set_contract_address(caller_2); - actions_system.buy_property(1, game_id); + actions_system.join_game(PlayerSymbol::Dog, 1); - let property = actions_system.get_property(1, game_id); - assert(property.owner == caller_2, 'invalid property txn'); - } + 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'); + let game_p = actions_system.retrieve_game(1); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 5); + + // print_players_positions(); + let aji = actions_system.retrieve_game_player(caller_1, 1); + let collins = actions_system.retrieve_game_player(caller_2, 1); + let jerry = actions_system.retrieve_game_player(caller_3, 1); + let aliyu = actions_system.retrieve_game_player(caller_4, 1); + + assert(aji.balance == 1500, 'Aji bal fail'); + assert(collins.balance == 1500, 'Collins bal fail'); + assert(jerry.balance == 1500, 'jerry bal fail'); + assert(aliyu.balance == 1500, 'aliyu bal fail'); + } #[test] - #[should_panic] - fn test_put_another_player_property_for_sale() { + fn test_generate_properties() { let caller_1 = contract_address_const::<'aji'>(); - let caller_2 = contract_address_const::<'ajidokwu'>(); + 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(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + 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, false, PlayerSymbol::Dog, 100); + 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); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); + 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); - actions_system.mint(caller_1, game_id, 10000); - actions_system.mint(caller_2, game_id, 10000); + 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'); - actions_system.buy_property(1, game_id); + let game_p = actions_system.retrieve_game(1); - testing::set_contract_address(caller_2); - actions_system.sell_property(1, game_id); + testing::set_contract_address(caller_1); + actions_system.move_player(1, 5); + let _property = actions_system.get_property(39, 1); } - #[test] - #[should_panic] - fn test_buy_property_thats_not_for_sale_from_a_player() { + fn test_move_handle_landing_buy_property_from_bank() { let caller_1 = contract_address_const::<'aji'>(); - let caller_2 = contract_address_const::<'ajidokwu'>(); + 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(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; - testing::set_contract_address(caller_1); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); + testing::set_contract_address(caller_2); + actions_system.register_new_player(username_1); testing::set_contract_address(caller_1); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); + actions_system.register_new_player(username); + + testing::set_contract_address(caller_3); + actions_system.register_new_player(username_2); - actions_system.mint(caller_1, game_id, 10000); - actions_system.mint(caller_2, game_id, 10000); + testing::set_contract_address(caller_4); + actions_system.register_new_player(username_3); testing::set_contract_address(caller_1); - actions_system.buy_property(1, game_id); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); testing::set_contract_address(caller_2); - actions_system.buy_property(1, game_id); + 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'); + + let game_p = actions_system.retrieve_game(1); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 5); + let ppt = actions_system.get_property(5, 1); + + let buyppt = actions_system.buy_property(ppt); - let property = actions_system.get_property(1, game_id); - assert(property.owner == caller_2, 'invalid property txn'); + assert(buyppt, 'Buy property failed'); + let aji = actions_system.retrieve_game_player(caller_1, 1); + + assert(aji.balance == 1300, 'debit failed'); + assert(*aji.properties_owned[0] == ppt.id, 'ownership transfer failed'); } #[test] - fn test_mortgage_property() { + fn test_pay_rent() { 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(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + 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, false, PlayerSymbol::Dog, 100); + 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); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); + + testing::set_contract_address(caller_2); + actions_system.join_game(PlayerSymbol::Dog, 1); - actions_system.mint(caller_1, game_id, 10000); + 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.buy_property(1, game_id); - let balance_before_mortgage = actions_system.get_players_balance(caller_1, game_id); - actions_system.mortgage_property(1, game_id); - let balance_after_mortgage = actions_system.get_players_balance(caller_1, game_id); - let property = actions_system.get_property(1, game_id); - println!( - "This is the before and this is the after before {} and after {}", - balance_after_mortgage, - balance_before_mortgage, - ); - assert(property.is_mortgaged, 'invalid is_mortgaged txn'); + actions_system.move_player(1, 5); + let ppt = actions_system.get_property(5, 1); + actions_system.buy_property(ppt); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 5); + let ppt1 = actions_system.get_property(5, 1); + + testing::set_contract_address(caller_2); + actions_system.pay_rent(ppt1); + + let aji = actions_system.retrieve_game_player(caller_1, 1); + assert(aji.balance == 1325, 'rent addition failed'); + + let collins = actions_system.retrieve_game_player(caller_2, 1); + assert(collins.balance == 1475, 'rent deduction failed'); } #[test] - #[should_panic] - fn test_mortgage_another_player_property() { + #[available_gas(9223372036854775807)] + fn test_rent_on_all_railways_owned_by_one_player() { let caller_1 = contract_address_const::<'aji'>(); - let caller_2 = contract_address_const::<'ajidokwu'>(); + 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(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + 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, false, PlayerSymbol::Dog, 100); + actions_system.register_new_player(username); testing::set_contract_address(caller_1); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); + 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, 5); + let mut property = actions_system.get_property(5, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 5); + property = actions_system.get_property(5, 1); + + testing::set_contract_address(caller_2); + actions_system.pay_rent(property); - actions_system.mint(caller_1, game_id, 10000); + // Assertion after one railway + let mut aji = actions_system.retrieve_game_player(caller_1, 1); + assert(aji.balance == 1325, 'rent addition failed'); + + let mut collins = actions_system.retrieve_game_player(caller_2, 1); + assert(collins.balance == 1475, 'rent deduction failed'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 10); + property = actions_system.get_property(15, 1); + actions_system.buy_property(property); + testing::set_contract_address(caller_2); + actions_system.move_player(1, 10); + property = actions_system.get_property(15, 1); + actions_system.pay_rent(property); + + // Assertion after two railways + aji = actions_system.retrieve_game_player(caller_1, 1); + assert(aji.balance == 1175, 'rent addition failed'); + + collins = actions_system.retrieve_game_player(caller_2, 1); + assert(collins.balance == 1425, 'rent deduction failed'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 10); + property = actions_system.get_property(25, 1); + actions_system.buy_property(property); + testing::set_contract_address(caller_2); + actions_system.move_player(1, 10); + property = actions_system.get_property(25, 1); + actions_system.pay_rent(property); + + // Assertion after three railways + aji = actions_system.retrieve_game_player(caller_1, 1); + assert(aji.balance == 1075, 'rent addition failed'); + + collins = actions_system.retrieve_game_player(caller_2, 1); + assert(collins.balance == 1325, 'rent deduction failed'); testing::set_contract_address(caller_1); - actions_system.buy_property(1, game_id); + actions_system.move_player(1, 10); + property = actions_system.get_property(35, 1); + actions_system.buy_property(property); testing::set_contract_address(caller_2); - actions_system.mortgage_property(1, game_id); + actions_system.move_player(1, 10); + property = actions_system.get_property(35, 1); + actions_system.pay_rent(property); + + // Assertion after four railways + aji = actions_system.retrieve_game_player(caller_1, 1); + assert(aji.balance == 1075, 'rent addition failed'); - let property = actions_system.get_property(1, game_id); - assert(property.is_mortgaged, 'invalid is_mortgaged txn'); + collins = actions_system.retrieve_game_player(caller_2, 1); + assert(collins.balance == 1125, 'rent deduction failed'); } #[test] - fn test_unmortgage_property() { + fn test_pay_on_two_utilities() { 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(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + 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, false, PlayerSymbol::Dog, 100); + actions_system.register_new_player(username); testing::set_contract_address(caller_1); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 2); + + testing::set_contract_address(caller_2); + actions_system.join_game(PlayerSymbol::Dog, 1); - actions_system.mint(caller_1, game_id, 10000); + 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.buy_property(1, game_id); - actions_system.mortgage_property(1, game_id); - let balance_before_unmortgage = actions_system.get_players_balance(caller_1, game_id); - actions_system.unmortgage_property(1, game_id); - let balance_after_unmortgage = actions_system.get_players_balance(caller_1, game_id); - let property = actions_system.get_property(1, game_id); - assert(!property.is_mortgaged, 'invalid is_mortgaged txn'); - } + actions_system.move_player(1, 12); + let mut property = actions_system.get_property(12, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 12); + property = actions_system.get_property(12, 1); + actions_system.pay_rent(property); + + // Assertion after one utility + let mut aji = actions_system.retrieve_game_player(caller_1, 1); + assert(aji.balance == 1398, 'rent addition failed'); + + let mut collins = actions_system.retrieve_game_player(caller_2, 1); + assert(collins.balance == 1452, 'rent deduction failed'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 16); + property = actions_system.get_property(28, 1); + actions_system.buy_property(property); + testing::set_contract_address(caller_2); + actions_system.move_player(1, 16); + property = actions_system.get_property(28, 1); + actions_system.pay_rent(property); + // Assertion after two utility + aji = actions_system.retrieve_game_player(caller_1, 1); + assert(aji.balance == 1408, 'rent addition failed'); + + collins = actions_system.retrieve_game_player(caller_2, 1); + assert(collins.balance == 1292, 'rent deduction failed'); + } #[test] - fn test_upgrade_property() { + fn test_get_200_pass_go() { 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(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + 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, false, PlayerSymbol::Dog, 100); + 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); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); + 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); - actions_system.mint(caller_1, game_id, 10000); + testing::set_contract_address(caller_4); + actions_system.join_game(PlayerSymbol::Iron, 1); testing::set_contract_address(caller_1); - actions_system.buy_property(1, game_id); + let started = actions_system.start_game(1); + assert(started, 'Game start fail'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 5); + let ppt = actions_system.get_property(5, 1); + actions_system.buy_property(ppt); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 49); - actions_system.buy_house_or_hotel(1, game_id); + let collins = actions_system.retrieve_game_player(caller_2, 1); - let property = actions_system.get_property(1, game_id); - assert(property.development == 1, 'invalid uy property txn'); + assert(collins.balance == 1700, '200 on go failed'); } #[test] - #[should_panic] - fn test_upgrade_someone_else_property() { + fn test_mortgage_and_unmortgage() { let caller_1 = contract_address_const::<'aji'>(); - let caller_2 = contract_address_const::<'ajidokwu'>(); + 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(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + let (contract_address, _) = world.dns(@"actions").unwrap(); + let actions_system = IActionsDispatcher { contract_address }; - testing::set_contract_address(caller_1); - actions_system.register_new_player(username, false, PlayerSymbol::Dog, 100); + testing::set_contract_address(caller_2); + actions_system.register_new_player(username_1); testing::set_contract_address(caller_1); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); + actions_system.register_new_player(username); - actions_system.mint(caller_1, game_id, 10000); - actions_system.mint(caller_2, game_id, 10000); + 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.buy_property(1, game_id); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); testing::set_contract_address(caller_2); - actions_system.buy_house_or_hotel(1, game_id); + 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, 5); + let ppt = actions_system.get_property(5, 1); + actions_system.buy_property(ppt); + + let ppt1 = actions_system.get_property(5, 1); + actions_system.mortgage_property(ppt1); + + let ppt11 = actions_system.get_property(5, 1); + + let aji = actions_system.retrieve_game_player(caller_1, 1); + assert(aji.balance == 1400, 'morgage inbursement failed'); + assert(ppt11.is_mortgaged, 'morgage failed'); + + let ppt2 = actions_system.get_property(5, 1); + actions_system.unmortgage_property(ppt2); + + let ppt21 = actions_system.get_property(5, 1); - let property = actions_system.get_property(1, game_id); - assert(property.development == 1, 'invalid uy property txn'); + let aji1 = actions_system.retrieve_game_player(caller_1, 1); + + assert(aji1.balance == 1290, 'morgage inbursement failed'); + assert(!ppt21.is_mortgaged, 'morgage failed'); + + assert(ppt11.is_mortgaged, 'morgage failed') } #[test] - #[should_panic] - fn test_upgrade_property_more_than_allowed() { + fn test_buy_houses_and_hotel_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(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + 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, false, PlayerSymbol::Dog, 100); + 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); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 4); - actions_system.mint(caller_1, game_id, 10000); + 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); - actions_system.buy_property(1, game_id); + 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); - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(1, game_id); + 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'); } #[test] - fn test_downgrade_property() { + fn test_pay_rent_on_site_only() { 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(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + 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, false, PlayerSymbol::Dog, 100); + actions_system.register_new_player(username); testing::set_contract_address(caller_1); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 2); - actions_system.mint(caller_1, game_id, 10000); + 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 failed"); + // Player 1 buys property at position 4 testing::set_contract_address(caller_1); - actions_system.buy_property(1, game_id); + actions_system.move_player(1, 1); + let mut property = actions_system.get_property(1, 1); + actions_system.buy_property(property); + + // Player 2 lands and pays rent + testing::set_contract_address(caller_2); + actions_system.move_player(1, 1); + let landed_property = actions_system.get_property(1, 1); + actions_system.pay_rent(landed_property); - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(1, game_id); - actions_system.sell_house_or_hotel(1, game_id); + // Player 1 buys property at position 4 + testing::set_contract_address(caller_1); + actions_system.move_player(1, 2); + let mut property = actions_system.get_property(3, 1); + actions_system.buy_property(property); - let property = actions_system.get_property(1, game_id); - assert(property.development == 2, 'invalid uy property txn'); + // Player 2 lands and pays rent + testing::set_contract_address(caller_2); + actions_system.move_player(1, 2); + let landed_property = actions_system.get_property(3, 1); + actions_system.pay_rent(landed_property); + + let aji = actions_system.retrieve_game_player(caller_1, 1); + let collins = actions_system.retrieve_game_player(caller_2, 1); + assert(aji.balance == 1386, 'Aji bal error'); + assert(collins.balance == 1494, 'Collins bal error'); + assert(property.development == 0, 'development error'); } #[test] - #[should_panic] - fn test_downgrade_someone_else_property() { + fn test_pay_rent_on_one_house() { let caller_1 = contract_address_const::<'aji'>(); - let caller_2 = contract_address_const::<'ajidokwu'>(); + 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(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + 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, false, PlayerSymbol::Dog, 100); + actions_system.register_new_player(username); testing::set_contract_address(caller_1); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); + 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 failed"); + + // SITE ONLY + 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); - actions_system.mint(caller_1, game_id, 10000); - actions_system.mint(caller_2, game_id, 10000); + // Player 2 lands and pays rent + testing::set_contract_address(caller_2); + actions_system.move_player(1, 1); + let landed_property = actions_system.get_property(1, 1); + actions_system.pay_rent(landed_property); testing::set_contract_address(caller_1); - actions_system.buy_property(1, game_id); + actions_system.move_player(1, 2); + let mut property = actions_system.get_property(3, 1); + actions_system.buy_property(property); testing::set_contract_address(caller_2); - actions_system.sell_house_or_hotel(1, game_id); + actions_system.move_player(1, 2); + let landed_property = actions_system.get_property(3, 1); + actions_system.pay_rent(landed_property); + + // ONE HOUSE + testing::set_contract_address(caller_1); + actions_system.move_player(1, 40); + let mut property = actions_system.get_property(3, 1); + let mut property1 = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); - let property = actions_system.get_property(1, game_id); - assert(property.development == 1, 'invalid uy property txn'); + let mut game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 40); + let landed_property = actions_system.get_property(3, 1); + actions_system.pay_rent(landed_property); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + let aji = actions_system.retrieve_game_player(caller_1, 1); + let collins = actions_system.retrieve_game_player(caller_2, 1); + + assert(aji.balance == 1506, 'Aji bal error'); + assert(collins.balance == 1674, 'Collins bal error'); + assert(property.development == 1, 'Property dev error'); + assert(property1.development == 1, 'Property dev error'); } #[test] - #[should_panic] - fn test_downgrade_property_more_than_allowed() { + #[available_gas(9223372036854775807)] + fn test_pay_rent_on_two_houses() { 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(@"world").unwrap(); - let actions_system = IWorldDispatcher { contract_address }; + 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, false, PlayerSymbol::Dog, 100); + actions_system.register_new_player(username); testing::set_contract_address(caller_1); - let game_id = actions_system.create_new_game(GameMode::MultiPlayer, PlayerSymbol::Hat, 4); - assert(game_id == 1, 'Wrong game id'); + actions_system.create_new_game(GameType::PublicGame, PlayerSymbol::Hat, 2); - actions_system.mint(caller_1, game_id, 10000); + testing::set_contract_address(caller_2); + actions_system.join_game(PlayerSymbol::Dog, 1); testing::set_contract_address(caller_1); - actions_system.buy_property(1, game_id); + let started = actions_system.start_game(1); + assert!(started, "Game start failed"); + + // SITE ONLY + + 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); + + // Player 2 lands and pays rent + testing::set_contract_address(caller_2); + actions_system.move_player(1, 1); + let landed_property = actions_system.get_property(1, 1); + actions_system.pay_rent(landed_property); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 2); + let mut property = actions_system.get_property(3, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 2); + let landed_property = actions_system.get_property(3, 1); + actions_system.pay_rent(landed_property); + + // ONE HOUSE + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 40); + let mut property = actions_system.get_property(3, 1); + let mut property1 = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + let mut game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 40); + let landed_property = actions_system.get_property(3, 1); + actions_system.pay_rent(landed_property); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + let aji = actions_system.retrieve_game_player(caller_1, 1); + let collins = actions_system.retrieve_game_player(caller_2, 1); + + assert(aji.balance == 1446, 'Aji bal error'); + assert(collins.balance == 1634, 'Collins bal error'); + assert(property.development == 2, 'Property dev error'); + assert(property1.development == 2, 'Property dev error'); + } + + #[test] + #[available_gas(9223372036854775807)] + fn test_pay_rent_on_three_houses() { + 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 failed"); + + // SITE ONLY + + 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); + + // Player 2 lands and pays rent + testing::set_contract_address(caller_2); + actions_system.move_player(1, 1); + let landed_property = actions_system.get_property(1, 1); + actions_system.pay_rent(landed_property); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 2); + let mut property = actions_system.get_property(3, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 2); + let landed_property = actions_system.get_property(3, 1); + actions_system.pay_rent(landed_property); + + // THREE HOUSES + testing::set_contract_address(caller_1); + actions_system.move_player(1, 40); + let mut property = actions_system.get_property(3, 1); + let mut property1 = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + let mut game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 40); + let landed_property = actions_system.get_property(3, 1); + actions_system.pay_rent(landed_property); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + let aji = actions_system.retrieve_game_player(caller_1, 1); + let collins = actions_system.retrieve_game_player(caller_2, 1); + + assert(aji.balance == 1466, 'Aji bal error'); + assert(collins.balance == 1514, 'Collins bal error'); + assert(property.development == 3, 'Property dev error'); + assert(property1.development == 3, 'Property dev error'); + } + + + #[test] + #[available_gas(9223372036854775807)] + fn test_pay_rent_on_four_houses() { + 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 failed"); + + // SITE ONLY + + 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); + + // Player 2 lands and pays rent + testing::set_contract_address(caller_2); + actions_system.move_player(1, 1); + let landed_property = actions_system.get_property(1, 1); + actions_system.pay_rent(landed_property); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 2); + let mut property = actions_system.get_property(3, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 2); + let landed_property = actions_system.get_property(3, 1); + actions_system.pay_rent(landed_property); + + // Four HOUSES + testing::set_contract_address(caller_1); + actions_system.move_player(1, 40); + let mut property = actions_system.get_property(1, 1); + let mut property1 = actions_system.get_property(3, 1); + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + let mut game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 40); + let landed_property = actions_system.get_property(3, 1); + actions_system.pay_rent(landed_property); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + let aji = actions_system.retrieve_game_player(caller_1, 1); + let collins = actions_system.retrieve_game_player(caller_2, 1); + + assert(aji.balance == 1506, 'Aji bal error'); + assert(collins.balance == 1374, 'Collins bal error'); + assert(property.development == 4, 'Property dev error'); + assert(property1.development == 4, 'Property dev error'); + } + + #[test] + #[available_gas(9223372036854775807)] + fn test_pay_rent_on_hotel() { + 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 failed"); + + // SITE ONLY + + 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); + + // Player 2 lands and pays rent + testing::set_contract_address(caller_2); + actions_system.move_player(1, 1); + let landed_property = actions_system.get_property(1, 1); + actions_system.pay_rent(landed_property); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 2); + let mut property = actions_system.get_property(3, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 2); + let landed_property = actions_system.get_property(3, 1); + actions_system.pay_rent(landed_property); + + // Four HOUSES + testing::set_contract_address(caller_1); + actions_system.move_player(1, 40); + let mut property = actions_system.get_property(3, 1); + let mut property1 = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + let mut game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 40); + let landed_property = actions_system.get_property(3, 1); + actions_system.pay_rent(landed_property); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + let aji = actions_system.retrieve_game_player(caller_1, 1); + let collins = actions_system.retrieve_game_player(caller_2, 1); + + assert(aji.balance == 1536, 'Aji bal error'); + assert(collins.balance == 1244, 'Collins bal error'); + assert(property.development == 5, 'Property dev error'); + assert(property1.development == 5, 'Property dev error'); + } + + #[test] + #[available_gas(9223372036854775807)] + #[should_panic] + fn test_pay_rent_on_six_houses() { + 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 failed"); + + // SITE ONLY + + 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); + + // Player 2 lands and pays rent + testing::set_contract_address(caller_2); + actions_system.move_player(1, 1); + let landed_property = actions_system.get_property(1, 1); + actions_system.pay_rent(landed_property); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 2); + let mut property = actions_system.get_property(3, 1); + actions_system.buy_property(property); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 2); + let landed_property = actions_system.get_property(4, 1); + actions_system.pay_rent(landed_property); + + // Four HOUSES + testing::set_contract_address(caller_1); + actions_system.move_player(1, 40); + let mut property = actions_system.get_property(3, 1); + let mut property1 = actions_system.get_property(1, 1); + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + property = actions_system.get_property(3, 1); + property1 = actions_system.get_property(1, 1); + + actions_system.buy_house_or_hotel(property); + actions_system.buy_house_or_hotel(property1); + + let mut game = actions_system.retrieve_game(1); + actions_system.finish_turn(game); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 40); + let landed_property = actions_system.get_property(3, 1); + actions_system.pay_rent(landed_property); + } + + #[test] + fn test_community_chest() { + 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, 2); + + let ppt = actions_system.get_property(5, 1); + let mut community = actions_system.handle_community_chest(1, 3); + println!("community chest 1 : {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 2: {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 3: {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 4: {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 5: {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 6: {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 7: {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 8: {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 9 : {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 10 : {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 11 : {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 12 : {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 13 : {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 14 : {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 15 : {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 16 : {}", community); + community = actions_system.handle_community_chest(1, 3); + println!("community chest 17 : {}", community); + } + + #[test] + fn test_community_chance() { + 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, 2); + + let ppt = actions_system.get_property(5, 1); + + let mut chance = actions_system.handle_chance(1, 3); + println!("chance 1 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 2 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 3 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 4 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 5 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 6 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 7 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 8 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 9 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 10 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 11 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 12 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 13 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 14 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 15 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 16 : {}", chance); + chance = actions_system.handle_chance(1, 3); + println!("chance 17 : {}", chance); + } + #[test] + fn test_process_only_chance() { + 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, 7); + + let mut g = actions_system.retrieve_game(1); + let mut p = actions_system.retrieve_game_player(caller_1, 1); + + let mut chance = actions_system.handle_chance(1, 3); + + let (game, ply) = actions_system.process_chance_card(g, p, chance.clone()); + + assert(ply.position == 12, 'position error'); + assert(ply.balance == 1430, 'bal error'); + } + + #[test] + fn test_process_chance_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, 7); + + let mut g = actions_system.retrieve_game(1); + let mut p = actions_system.retrieve_game_player(caller_1, 1); + + let mut chance: ByteArray = "Advance to Go (Collect $200)"; + + let (_, ply) = actions_system.process_chance_card(g.clone(), p, chance); + 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, 7); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + chance = "Advance to MakerDAO Avenue - If you pass Go, collect $200"; + + let (_, ply) = actions_system.process_chance_card(g.clone(), p, chance); + g = actions_system.finish_turn(g); + + assert(ply.position == 24, 'position error'); + assert(ply.balance == 1500, 'bal error'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 7); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + chance = "Advance to Arbitrium Avenue - If you pass Go, collect $200"; + + let (_, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 11, 'position error'); + assert(ply.balance == 1700, 'bal error'); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 12); + + g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + chance = "Advance token to nearest Utility. Pay 10x dice."; + + let (g, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 12, 'position error'); + assert(ply.balance == 1380, 'bal error'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 11); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + chance = "Advance token to nearest Railroad. Pay 2x rent."; + + let (g, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 25, 'position error'); + assert(ply.balance == 1700, 'bal error'); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 10); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + chance = "Bank pays you dividend of $50"; + + let (g, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 22, 'position error'); + assert(ply.balance == 1430, 'bal error'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 11); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + chance = "Get out of Jail Free"; + + let (g, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 36, 'position error'); + assert(ply.balance == 1700, 'bal error'); + assert(ply.chance_jail_card, 'get out jail error'); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 14); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + chance = "Pay poor tax of $15"; + + let (g, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 36, 'position error'); + assert(ply.balance == 1415, 'bal error'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 11); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + chance = "Make general repairs - $25 house, $100 hotel"; + + let (g, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 7, 'position error'); + assert(ply.balance == 1900, 'bal error'); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 11); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + chance = "Go Back 3 Spaces"; + + let (g, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 4, 'position error'); + assert(ply.balance == 1615, '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); + + chance = "Make general repairs - $25 house, $100 hotel"; + + let (g, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 22, 'position error'); + assert(ply.balance == 1900, 'bal error'); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 3); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + chance = "Take a trip to IPFS Railroad"; + + let (g, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 5, 'position error'); + assert(ply.balance == 1815, 'bal error'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 14); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + chance = "Take a walk on the Bitcoin Lane"; + + let (g, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 39, 'position error'); + assert(ply.balance == 1900, 'bal error'); + + testing::set_contract_address(caller_2); + actions_system.move_player(1, 2); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_2, 1); + + chance = "Speeding fine $200"; + + let (g, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 7, 'position error'); + assert(ply.balance == 1615, 'bal error'); + + testing::set_contract_address(caller_1); + actions_system.move_player(1, 8); + + let g = actions_system.retrieve_game(1); + p = actions_system.retrieve_game_player(caller_1, 1); + + chance = "Building loan matures - collect $150"; + + let (g, ply) = actions_system.process_chance_card(g.clone(), p, chance); + actions_system.finish_turn(g); + + assert(ply.position == 7, 'position error'); + assert(ply.balance == 2250, 'bal error'); + } + + #[test] + fn test_process_community_chest() { + 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, 2); + + let mut g = actions_system.retrieve_game(1); + + let mut p = actions_system.retrieve_game_player(caller_1, 1); + + let mut community_chest = actions_system.handle_community_chest(1, 2); + + let (_, ply) = actions_system.process_community_chest_card(g, p, community_chest.clone()); + + assert(ply.position == 2, 'position error'); + assert(ply.balance == 1450, 'bal error'); + } +} - actions_system.buy_house_or_hotel(1, game_id); - actions_system.buy_house_or_hotel(1, game_id); - actions_system.sell_house_or_hotel(1, game_id); - actions_system.sell_house_or_hotel(1, game_id); - actions_system.sell_house_or_hotel(1, game_id); - } -} diff --git a/src/utils/property_templates.cairo b/src/utils/property_templates.cairo deleted file mode 100644 index 3b54f21..0000000 --- a/src/utils/property_templates.cairo +++ /dev/null @@ -1,201 +0,0 @@ -use core::array::Array; - - -#[derive(Copy, Drop, Serde)] -pub struct PropertyTemplate { - pub id: u8, - pub name: felt252, - pub cost: u256, - pub rent_site_only: u256, - pub rent_one_house: u256, - pub rent_two_houses: u256, - pub rent_three_houses: u256, - pub rent_four_houses: u256, - pub cost_of_house: u256, - pub rent_hotel: u256, - pub group_id: u8, -} - -pub trait PropertyTemplatesImpl { - fn get_standard_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - >; - fn get_brown_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - >; - fn get_light_blue_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - >; - fn get_pink_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - >; - fn get_orange_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - >; - fn get_red_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - >; - fn get_yellow_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - >; - fn get_green_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - >; - fn get_blue_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - >; - fn get_railroads() -> Array<(u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8)>; - fn get_utilities() -> Array<(u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8)>; -} - -impl PropertyTemplates of PropertyTemplatesImpl { - fn get_standard_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - > { - let mut properties = ArrayTrait::new(); - - // Brown Properties (Group 1) - properties.append((1, 'Mediterranean_Ave', 60, 2, 10, 30, 90, 160, 50, 250, 1)); - properties.append((3, 'Baltic_Ave', 60, 4, 20, 60, 180, 320, 50, 450, 1)); - - // Light Blue Properties (Group 2) - properties.append((6, 'Oriental_Ave', 100, 6, 30, 90, 270, 400, 50, 550, 2)); - properties.append((8, 'Vermont_Ave', 100, 6, 30, 90, 270, 400, 50, 550, 2)); - properties.append((9, 'Connecticut_Ave', 120, 8, 40, 100, 300, 450, 50, 600, 2)); - - // Pink Properties (Group 3) - properties.append((11, 'St_Charles_Place', 140, 10, 50, 150, 450, 625, 100, 750, 3)); - properties.append((13, 'States_Ave', 140, 10, 50, 150, 450, 625, 100, 750, 3)); - properties.append((14, 'Virginia_Ave', 160, 12, 60, 180, 500, 700, 100, 900, 3)); - - // Orange Properties (Group 4) - properties.append((16, 'St_James_Place', 180, 14, 70, 200, 550, 750, 100, 950, 4)); - properties.append((18, 'Tennessee_Ave', 180, 14, 70, 200, 550, 750, 100, 950, 4)); - properties.append((19, 'New_York_Ave', 200, 16, 80, 220, 600, 800, 100, 1000, 4)); - - // Red Properties (Group 5) - properties.append((21, 'Kentucky_Ave', 220, 18, 90, 250, 700, 875, 150, 1050, 5)); - properties.append((23, 'Indiana_Ave', 220, 18, 90, 250, 700, 875, 150, 1050, 5)); - properties.append((24, 'Illinois_Ave', 240, 20, 100, 300, 750, 925, 150, 1100, 5)); - - // Yellow Properties (Group 6) - properties.append((26, 'Atlantic_Ave', 260, 22, 110, 330, 800, 975, 150, 1150, 6)); - properties.append((27, 'Ventnor_Ave', 260, 22, 110, 330, 800, 975, 150, 1150, 6)); - properties.append((29, 'Marvin_Gardens', 280, 24, 120, 360, 850, 1025, 150, 1200, 6)); - - // Green Properties (Group 7) - properties.append((31, 'Pacific_Ave', 300, 26, 130, 390, 900, 1100, 200, 1275, 7)); - properties.append((32, 'North_Carolina_Ave', 300, 26, 130, 390, 900, 1100, 200, 1275, 7)); - properties.append((34, 'Pennsylvania_Ave', 320, 28, 150, 450, 1000, 1200, 200, 1400, 7)); - - // Blue Properties (Group 8) - properties.append((37, 'Park_Place', 350, 35, 175, 500, 1100, 1300, 200, 1500, 8)); - properties.append((39, 'Boardwalk', 400, 50, 200, 600, 1400, 1700, 200, 2000, 8)); - - // Railroads (Group 9) - properties.append((5, 'Reading_Railroad', 200, 25, 50, 100, 200, 0, 0, 0, 9)); - properties.append((15, 'Pennsylvania_Railroad', 200, 25, 50, 100, 200, 0, 0, 0, 9)); - properties.append((25, 'BO_Railroad', 200, 25, 50, 100, 200, 0, 0, 0, 9)); - properties.append((35, 'Short_Line', 200, 25, 50, 100, 200, 0, 0, 0, 9)); - - // Utilities (Group 10) - properties.append((12, 'Electric_Company', 150, 4, 10, 0, 0, 0, 0, 0, 10)); - properties.append((28, 'Water_Works', 150, 4, 10, 0, 0, 0, 0, 0, 10)); - - properties - } - - fn get_brown_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - > { - let mut properties = ArrayTrait::new(); - properties.append((1, 'Mediterranean_Ave', 60, 2, 10, 30, 90, 160, 50, 250, 1)); - properties.append((3, 'Baltic_Ave', 60, 4, 20, 60, 180, 320, 50, 450, 1)); - properties - } - - fn get_light_blue_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - > { - let mut properties = ArrayTrait::new(); - properties.append((6, 'Oriental_Ave', 100, 6, 30, 90, 270, 400, 50, 550, 2)); - properties.append((8, 'Vermont_Ave', 100, 6, 30, 90, 270, 400, 50, 550, 2)); - properties.append((9, 'Connecticut_Ave', 120, 8, 40, 100, 300, 450, 50, 600, 2)); - properties - } - - fn get_pink_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - > { - let mut properties = ArrayTrait::new(); - properties.append((11, 'St_Charles_Place', 140, 10, 50, 150, 450, 625, 100, 750, 3)); - properties.append((13, 'States_Ave', 140, 10, 50, 150, 450, 625, 100, 750, 3)); - properties.append((14, 'Virginia_Ave', 160, 12, 60, 180, 500, 700, 100, 900, 3)); - properties - } - - fn get_orange_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - > { - let mut properties = ArrayTrait::new(); - properties.append((16, 'St_James_Place', 180, 14, 70, 200, 550, 750, 100, 950, 4)); - properties.append((18, 'Tennessee_Ave', 180, 14, 70, 200, 550, 750, 100, 950, 4)); - properties.append((19, 'New_York_Ave', 200, 16, 80, 220, 600, 800, 100, 1000, 4)); - properties - } - - fn get_red_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - > { - let mut properties = ArrayTrait::new(); - properties.append((21, 'Kentucky_Ave', 220, 18, 90, 250, 700, 875, 150, 1050, 5)); - properties.append((23, 'Indiana_Ave', 220, 18, 90, 250, 700, 875, 150, 1050, 5)); - properties.append((24, 'Illinois_Ave', 240, 20, 100, 300, 750, 925, 150, 1100, 5)); - properties - } - - fn get_yellow_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - > { - let mut properties = ArrayTrait::new(); - properties.append((26, 'Atlantic_Ave', 260, 22, 110, 330, 800, 975, 150, 1150, 6)); - properties.append((27, 'Ventnor_Ave', 260, 22, 110, 330, 800, 975, 150, 1150, 6)); - properties.append((29, 'Marvin_Gardens', 280, 24, 120, 360, 850, 1025, 150, 1200, 6)); - properties - } - - fn get_green_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - > { - let mut properties = ArrayTrait::new(); - properties.append((31, 'Pacific_Ave', 300, 26, 130, 390, 900, 1100, 200, 1275, 7)); - properties.append((32, 'North_Carolina_Ave', 300, 26, 130, 390, 900, 1100, 200, 1275, 7)); - properties.append((34, 'Pennsylvania_Ave', 320, 28, 150, 450, 1000, 1200, 200, 1400, 7)); - properties - } - - fn get_blue_properties() -> Array< - (u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8), - > { - let mut properties = ArrayTrait::new(); - properties.append((37, 'Park_Place', 350, 35, 175, 500, 1100, 1300, 200, 1500, 8)); - properties.append((39, 'Boardwalk', 400, 50, 200, 600, 1400, 1700, 200, 2000, 8)); - properties - } - - fn get_railroads() -> Array<(u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8)> { - let mut properties = ArrayTrait::new(); - properties.append((5, 'Reading_Railroad', 200, 25, 50, 100, 200, 0, 0, 0, 9)); - properties.append((15, 'Pennsylvania_Railroad', 200, 25, 50, 100, 200, 0, 0, 0, 9)); - properties.append((25, 'BO_Railroad', 200, 25, 50, 100, 200, 0, 0, 0, 9)); - properties.append((35, 'Short_Line', 200, 25, 50, 100, 200, 0, 0, 0, 9)); - properties - } - - fn get_utilities() -> Array<(u8, felt252, u256, u256, u256, u256, u256, u256, u256, u256, u8)> { - let mut properties = ArrayTrait::new(); - properties.append((12, 'Electric_Company', 150, 4, 10, 0, 0, 0, 0, 0, 10)); - properties.append((28, 'Water_Works', 150, 4, 10, 0, 0, 0, 0, 0, 10)); - properties - } -}