diff --git a/onchain/src/models/game.cairo b/onchain/src/models/game.cairo index a9dba46..9686a16 100644 --- a/onchain/src/models/game.cairo +++ b/onchain/src/models/game.cairo @@ -5,11 +5,10 @@ use starkludo::models::player::{Player}; // Can either be Ongoing or Ended #[derive(Serde, Copy, Drop, Introspect, PartialEq, Debug)] pub enum GameStatus { - Initialised, - Pending, - Ongoing, - Waiting, - Ended, + Initialised, // Game has been created + Pending, // Waiting for players to join (in multiplayer mode) + Ongoing, // Game is ongoing + Ended // Game has ended } // Represents the game mode @@ -17,7 +16,7 @@ pub enum GameStatus { #[derive(Serde, Copy, Drop, Introspect, PartialEq)] pub enum GameMode { SinglePlayer, // Play with computer - MultiPlayer, // Play online with friends + MultiPlayer // Play online with friends } #[derive(Serde, Copy, Drop, Introspect, PartialEq)] @@ -25,7 +24,7 @@ pub enum PlayerColor { Green, Yellow, Blue, - Red + Red, } // Game model @@ -70,7 +69,7 @@ pub struct Game { pub b0: felt252, // blue piece position on board pub b1: felt252, // blue piece position on board pub b2: felt252, // blue piece position on board - pub b3: felt252, // blue piece position on board + pub b3: felt252 // blue piece position on board } pub trait GameTrait { @@ -83,7 +82,7 @@ pub trait GameTrait { player_blue: felt252, player_yellow: felt252, player_green: felt252, - number_of_players: u8 + number_of_players: u8, ) -> Game; fn restart(ref self: Game); fn terminate_game(ref self: Game); @@ -98,7 +97,7 @@ impl GameImpl of GameTrait { player_blue: felt252, player_yellow: felt252, player_green: felt252, - number_of_players: u8 + number_of_players: u8, ) -> Game { let zero_address = contract_address_const::<0x0>(); Game { @@ -122,14 +121,31 @@ impl GameImpl of GameTrait { dice_face: 0, player_chance: zero_address.into(), has_thrown_dice: false, - game_condition: array![0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32, 0_u32], + game_condition: array![ + 0_u32, + 0_u32, + 0_u32, + 0_u32, + 0_u32, + 0_u32, + 0_u32, + 0_u32, + 0_u32, + 0_u32, + 0_u32, + 0_u32, + 0_u32, + 0_u32, + 0_u32, + 0_u32, + ], r0: match number_of_players { 0 => panic!("number of players cannot be 0"), 1 => panic!("number of players cannot be 1"), 2 => 'R01', 3 => 'R01', 4 => 'R01', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, r1: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -137,7 +153,7 @@ impl GameImpl of GameTrait { 2 => 'R02', 3 => 'R02', 4 => 'R02', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, r2: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -145,7 +161,7 @@ impl GameImpl of GameTrait { 2 => 'R03', 3 => 'R03', 4 => 'R03', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, r3: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -153,7 +169,7 @@ impl GameImpl of GameTrait { 2 => 'R04', 3 => 'R04', 4 => 'R04', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, g0: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -161,7 +177,7 @@ impl GameImpl of GameTrait { 2 => 'G01', 3 => 'G01', 4 => 'G01', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, g1: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -169,7 +185,7 @@ impl GameImpl of GameTrait { 2 => 'G02', 3 => 'G02', 4 => 'G02', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, g2: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -177,7 +193,7 @@ impl GameImpl of GameTrait { 2 => 'G03', 3 => 'G03', 4 => 'G03', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, g3: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -185,7 +201,7 @@ impl GameImpl of GameTrait { 2 => 'GO4', 3 => 'GO4', 4 => 'GO4', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, y0: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -193,7 +209,7 @@ impl GameImpl of GameTrait { 2 => 0, 3 => 'Y01', 4 => 'Y01', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, y1: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -201,7 +217,7 @@ impl GameImpl of GameTrait { 2 => 0, 3 => 'Y02', 4 => 'Y02', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, y2: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -209,7 +225,7 @@ impl GameImpl of GameTrait { 2 => 0, 3 => 'Y03', 4 => 'Y03', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, y3: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -217,7 +233,7 @@ impl GameImpl of GameTrait { 2 => 0, 3 => 'Y04', 4 => 'Y04', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, b0: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -225,7 +241,7 @@ impl GameImpl of GameTrait { 2 => 0, 3 => 0, 4 => 'B01', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, b1: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -233,7 +249,7 @@ impl GameImpl of GameTrait { 2 => 0, 3 => 0, 4 => 'B02', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, b2: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -241,7 +257,7 @@ impl GameImpl of GameTrait { 2 => 0, 3 => 0, 4 => 'B03', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, b3: match number_of_players { 0 => panic!("number of players cannot be 0"), @@ -249,7 +265,7 @@ impl GameImpl of GameTrait { 2 => 0, 3 => 0, 4 => 'B04', - _ => panic!("invalid number of players") + _ => panic!("invalid number of players"), }, } } diff --git a/onchain/src/systems/game_actions.cairo b/onchain/src/systems/game_actions.cairo index 53b8b67..398ed6e 100644 --- a/onchain/src/systems/game_actions.cairo +++ b/onchain/src/systems/game_actions.cairo @@ -11,14 +11,13 @@ trait IGameActions { ) -> u64; fn start_game(ref self: T, game_id: u64); fn join(ref self: T, username: felt252, selected_color: felt252, game_id: u64); - fn move(ref self: T, pos: felt252, color: u8, game_id: u64); + fn move(ref self: T, pos: felt252, color: u8); fn roll(ref self: T) -> (u8, u8); fn get_current_game_id(self: @T) -> u64; fn create_new_game_id(ref self: T) -> u64; fn create_new_player(ref self: T, username: felt252, is_bot: bool); - fn create_bot_player(ref self: T, bot_color: PlayerColor) -> Player; fn get_username_from_address(self: @T, address: ContractAddress) -> felt252; fn get_address_from_username(self: @T, username: felt252) -> ContractAddress; fn move_deducer(ref self: T, val: u32, dice_throw: u32) -> (u32, bool, bool); @@ -130,11 +129,13 @@ pub mod GameActions { game_id } - fn start_game(ref self: ContractState, game_id: u64) { - // Get game world + /// Start game + /// Change game status to ONGOING + fn join(ref self: ContractState, player_color: PlayerColor, game_id: u64) { + // Get world state let mut world = self.world_default(); - // Get game from world + //get the game state let mut game: Game = world.read_model(game_id); assert(game.is_initialised, 'GAME NOT INITIALISED'); @@ -146,93 +147,118 @@ pub mod GameActions { // Check if all the players have joined the game - let game_mode: GameMode = game.mode; + // Assert that game is a Multiplayer game + assert(game.mode == GameMode::MultiPlayer, 'GAME NOT MULTIPLAYER'); + + // Assert that game is in Pending state + assert(game.status == GameStatus::Pending, 'GAME NOT PENDING'); + + // 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'); + + /// Game starts automatically once the last player joins + + // Verify that color is available + // Assign color to player if available + match player_color { + PlayerColor::Red => { + if (game.player_red == 0) { + game.player_red = caller_username + } else { + panic!("RED already selected"); + } + }, + PlayerColor::Blue => { + if (game.player_blue == 0) { + game.player_blue = caller_username + } else { + panic!("BLUE already selected"); + } + }, + PlayerColor::Green => { + if (game.player_green == 0) { + game.player_green = caller_username + } else { + panic!("GREEN already selected"); + } + }, + PlayerColor::Yellow => { + if (game.player_yellow == 0) { + game.player_yellow = caller_username + } else { + panic!("YELLOW already selected"); + } + }, + } + + // Start game automatically once the last player joins + + const TWO_PLAYERS: u8 = 2; + const THREE_PLAYERS: u8 = 3; + const FOUR_PLAYERS: u8 = 4; - // Create bot players (if they have notbeen created) + // Create bot players (if they have not been created) let green_bot: Player = self.create_bot_player(PlayerColor::Green); let yellow_bot: Player = self.create_bot_player(PlayerColor::Yellow); let blue_bot: Player = self.create_bot_player(PlayerColor::Blue); let red_bot: Player = self.create_bot_player(PlayerColor::Red); - match game_mode { - GameMode::SinglePlayer => { - // Check for color the player selected - if game.player_green != 0 { - if game.number_of_players == 4 { - game.player_yellow == yellow_bot.username; - game.player_blue == blue_bot.username; - game.player_red == red_bot.username; - } else if game.number_of_players == 3 { - game.player_yellow == yellow_bot.username; - game.player_blue == blue_bot.username; - } else if game.number_of_players == 2 { - game.player_yellow == yellow_bot.username; - } - } else if game.player_yellow != 0 { - if game.number_of_players == 4 { - game.player_blue == blue_bot.username; - game.player_red == red_bot.username; - game.player_green == green_bot.username; - } else if game.number_of_players == 3 { - game.player_blue == blue_bot.username; - game.player_green == green_bot.username; - } else if game.number_of_players == 2 { - game.player_green == green_bot.username; - } - } else if game.player_red != 0 { - if game.number_of_players == 4 { - game.player_yellow == yellow_bot.username; - game.player_blue == blue_bot.username; - game.player_green == green_bot.username; - } else if game.number_of_players == 3 { - game.player_green == green_bot.username; - game.player_blue == blue_bot.username; - } else if game.number_of_players == 2 { - game.player_blue == blue_bot.username; - } - } else if game.player_blue != 0 { - if game.number_of_players == 4 { - game.player_yellow == yellow_bot.username; - game.player_green == green_bot.username; - game.player_red == red_bot.username; - } else if game.number_of_players == 3 { - game.player_yellow == yellow_bot.username; - game.player_blue == blue_bot.username; - } else if game.number_of_players == 2 { - game.player_blue == blue_bot.username; - } + if (game.player_red != 0) { + players_joined_count += 1; + } + if (game.player_blue != 0) { + players_joined_count += 1; + } + if (game.player_green != 0) { + players_joined_count += 1; + } + if (game.player_yellow != 0) { + players_joined_count += 1; + } + + // Start game once all players have joined + if (players_joined_count == TWO_PLAYERS) { + game.status = GameStatus::Ongoing; } }, - GameMode::MultiPlayer => {}, + GameMode::MultiPlayer => {} }; } - fn join(ref self: ContractState, username: felt252, selected_color: felt252, game_id: u64) { - // Get world state - let mut world = self.world_default(); + // Start game once all players have joined + if (players_joined_count == THREE_PLAYERS) { + game.status = GameStatus::Ongoing; + } + }, + 4 => { + let mut players_joined_count: u8 = 0; //get the game state let mut game: Game = world.read_model(game_id); + // game.player_red = match selected_color { 0 => 0, 1 => username, - _ => 0, + _ => 0 }; game.player_yellow = match selected_color { 0 => 0, 1 => username, - _ => 0, + _ => 0 }; game.player_blue = match selected_color { 0 => 0, 1 => username, - _ => 0, + _ => 0 }; game.player_green = match selected_color { 0 => 0, 1 => username, - _ => 0, + _ => 0 }; } @@ -604,7 +630,7 @@ pub mod GameActions { PlayerColor::Red => { username = 'red_bot'; player_address = contract_address_const::<'red_bot'>(); - }, + } }; let existing_player: Player = world.read_model(username); @@ -615,10 +641,10 @@ pub mod GameActions { let new_player: Player = PlayerTrait::new(username, player_address, is_bot); let username_to_address: UsernameToAddress = UsernameToAddress { - username, address: player_address, + username, address: player_address }; let address_to_username: AddressToUsername = AddressToUsername { - address: player_address, username, + address: player_address, username }; world.write_model(@new_player); diff --git a/onchain/src/tests/test_game.cairo b/onchain/src/tests/test_game.cairo index bf46081..15db080 100644 --- a/onchain/src/tests/test_game.cairo +++ b/onchain/src/tests/test_game.cairo @@ -189,65 +189,6 @@ mod tests { assert_eq!(address_to_username.username, username); } - #[test] - fn test_create_bot_player_is_successful() { - let (_, game_action_system) = setup_world(); - - let blue_color = PlayerColor::Blue; - let green_color = PlayerColor::Green; - let red_color = PlayerColor::Red; - let yellow_color = PlayerColor::Yellow; - - let created_blue_bot_player: Player = game_action_system.create_bot_player(blue_color); - let expected_blue_username = 'blue_bot'; - let expected_blue_address = starknet::contract_address_const::<'blue_bot'>(); - assert_eq!(created_blue_bot_player.username, expected_blue_username); - assert_eq!(created_blue_bot_player.owner, expected_blue_address); - assert_eq!(created_blue_bot_player.is_bot, true); - - let created_green_bot_player: Player = game_action_system.create_bot_player(green_color); - let expected_green_username = 'green_bot'; - let expected_green_address = starknet::contract_address_const::<'green_bot'>(); - assert_eq!(created_green_bot_player.username, expected_green_username); - assert_eq!(created_green_bot_player.owner, expected_green_address); - assert_eq!(created_green_bot_player.is_bot, true); - - let created_red_bot_player: Player = game_action_system.create_bot_player(red_color); - let expected_red_username = 'red_bot'; - let expected_red_address = starknet::contract_address_const::<'red_bot'>(); - assert_eq!(created_red_bot_player.username, expected_red_username); - assert_eq!(created_red_bot_player.owner, expected_red_address); - assert_eq!(created_red_bot_player.is_bot, true); - - let created_yellow_bot_player: Player = game_action_system.create_bot_player(yellow_color); - let expected_yellow_username = 'yellow_bot'; - let expected_yellow_address = starknet::contract_address_const::<'yellow_bot'>(); - assert_eq!(created_yellow_bot_player.username, expected_yellow_username); - assert_eq!(created_yellow_bot_player.owner, expected_yellow_address); - assert_eq!(created_yellow_bot_player.is_bot, true); - } - - #[test] - fn test_create_existing_bot_player() { - let (_, game_action_system) = setup_world(); - - let blue_color = PlayerColor::Blue; - let created_blue_bot_player: Player = game_action_system.create_bot_player(blue_color); - - let expected_blue_username = 'blue_bot'; - let expected_blue_address = starknet::contract_address_const::<'blue_bot'>(); - assert_eq!(created_blue_bot_player.username, expected_blue_username); - assert_eq!(created_blue_bot_player.owner, expected_blue_address); - assert_eq!(created_blue_bot_player.is_bot, true); - - let existing_blue_bot_player: Player = game_action_system.create_bot_player(blue_color); - - assert_eq!(existing_blue_bot_player.username, expected_blue_username); - assert_eq!(existing_blue_bot_player.owner, expected_blue_address); - assert_eq!(existing_blue_bot_player.is_bot, true); - assert_eq!(created_blue_bot_player.owner, existing_blue_bot_player.owner); - } - #[test] fn test_get_username_from_address() { let ndef = namespace_def(); @@ -484,7 +425,6 @@ mod tests { // Setup initial game state let game_id = game_action_system .create_new_game(GameMode::MultiPlayer, PlayerColor::Red, 2); - game_action_system.start_game(game_id); let mut game: Game = world.read_model(game_id); @@ -520,7 +460,6 @@ mod tests { // Setup initial game state let game_id = game_action_system .create_new_game(GameMode::MultiPlayer, PlayerColor::Red, 2); - game_action_system.start_game(game_id); let mut game: Game = world.read_model(game_id); @@ -554,7 +493,6 @@ mod tests { // Setup initial game state let game_id = game_action_system .create_new_game(GameMode::MultiPlayer, PlayerColor::Red, 2); - game_action_system.start_game(game_id); let mut game: Game = world.read_model(game_id); game.dice_face = 6; @@ -591,7 +529,6 @@ mod tests { // Setup initial game state let game_id = game_action_system .create_new_game(GameMode::MultiPlayer, PlayerColor::Green, 2); - game_action_system.start_game(game_id); let mut game: Game = world.read_model(game_id); game.dice_face = 6; @@ -633,7 +570,6 @@ mod tests { // Setup initial game state let game_id = game_action_system .create_new_game(GameMode::MultiPlayer, PlayerColor::Green, 2); - game_action_system.start_game(game_id); let mut game: Game = world.read_model(game_id); game.dice_face = 6; @@ -682,7 +618,6 @@ mod tests { // Setup initial game state let game_id = game_action_system .create_new_game(GameMode::MultiPlayer, PlayerColor::Red, 2); - game_action_system.start_game(game_id); let mut game: Game = world.read_model(game_id); game.game_condition = array![13, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; @@ -730,7 +665,6 @@ mod tests { // Setup initial game state let game_id = game_action_system .create_new_game(GameMode::MultiPlayer, PlayerColor::Red, 2); - game_action_system.start_game(game_id); let mut game: Game = world.read_model(game_id); game.game_condition = array![13, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; @@ -777,7 +711,6 @@ mod tests { // Setup initial game state let game_id = game_action_system .create_new_game(GameMode::MultiPlayer, PlayerColor::Red, 2); - game_action_system.start_game(game_id); // Move red piece to position 56 (one step away from winning) let mut game: Game = world.read_model(game_id);