From 54a6164e6cba57551e3778f8f55ebed1fef12e07 Mon Sep 17 00:00:00 2001 From: Nami Reghbati Date: Wed, 27 Nov 2024 15:46:31 -0500 Subject: [PATCH 01/15] add: Added MEVBoost, SoftFork & HardFork + bug fixes. --- src/models/actions.cairo | 180 +++++++++ src/models/components.cairo | 66 ++-- src/models/enums.cairo | 70 +++- src/models/lib.cairo | 5 +- src/models/structs.cairo | 206 ++--------- src/models/traits.cairo | 719 +++++++++++++++++++++++++----------- 6 files changed, 799 insertions(+), 447 deletions(-) create mode 100644 src/models/actions.cairo diff --git a/src/models/actions.cairo b/src/models/actions.cairo new file mode 100644 index 0000000..42fcbe0 --- /dev/null +++ b/src/models/actions.cairo @@ -0,0 +1,180 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// ______ __ __ ______ ______ //////////////////////////////// +//////////////////////////////// /\___ \/\ \/ / /\__ _\/\__ _\ //////////////////////////////// +//////////////////////////////// \/_/ /_\ \ _`-.\/_/\ \/\/_/\ \/ //////////////////////////////// +//////////////////////////////// /\_____\ \_\ \_\ \ \_\ \ \_\ //////////////////////////////// +//////////////////////////////// \/_____/\/_/\/_/ \/_/ \/_/ //////////////////////////////// +//////////////////////////////// //////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// + + +use starknet::ContractAddress; +use zktt::models::structs::{StructBlockchain, StructAssetGroup}; +use zktt::models::enums::{EnumGasFeeType, EnumPlayerTarget}; + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +/////////////////////////////// ACTIONS ///////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +/// Swap a single blockchain with a player. +/// +/// Fields: +/// *m_self_blockchain_name*: The name of the blockchain from the caller to be swapped. +/// *m_opponent_blockchain_name*: The name of the blockchain to look up from the opponent to be +/// swapped. +/// *m_value*: Value of the card itself, in case we want to give it as eth. +/// *m_index*: The card index from all of its duplicates in the deck. +#[derive(Drop, Serde, Clone, Introspect, PartialEq, Debug)] +struct ActionChainReorg { + m_self_blockchain_name: ByteArray, + m_opponent_blockchain_name: ByteArray, + m_opponent_address: ContractAddress, + m_value: u8, + m_index: u8 +} + +/// All other players pay you 2 ETH. +/// +/// Fields: +/// *m_value*: Value of the card itself, in case we want to give it as eth. +/// *m_index*: The card index from all of its duplicates in the deck. +#[derive(Drop, Serde, Clone, Introspect, PartialEq, Debug)] +struct ActionClaimYield { + m_value: u8, + m_index: u8 +} + +/// Force any player to pay you 5 ETH. +/// +/// Fields: +/// *m_player_targeted*: The player forced to pay. +/// *m_value*: Value of the card itself, in case we want to give it as eth. +/// *m_index*: The card index from all of its duplicates in the deck. +#[derive(Drop, Serde, Clone, Introspect, PartialEq, Debug)] +struct ActionSandwichAttack { + m_player_targeted: ContractAddress, + m_value: u8, + m_index: u8 +} + +/// Steal an asset group from an opponent. +/// +/// Fields: +/// *m_player_targeted*: The player having to give up an asset group. +/// *m_set*: A array of blockchain names, pointing to which blockchains should this apply for. +/// *m_value*: Value of the card itself, in case we want to give it as eth. +/// *m_index*: The card index from all of its duplicates in the deck. +#[derive(Drop, Serde, Clone, Introspect, Debug)] +struct ActionFiftyOnePercentAttack { + m_player_targeted: ContractAddress, + m_set: Array, + m_value: u8, + m_index: u8 +} + +/// Card that allows a player to steal a blockchain from another player's deck. +/// +/// Fields: +/// *m_player_targeted*: Player having to let go of the blockchain. +/// *m_blockchain_name*: Name of the card to be stolen. +/// *m_value*: Value of the card itself, in case we want to give it as eth. +/// *m_index*: The card index from all of its duplicates in the deck. +#[derive(Drop, Serde, Clone, Introspect, Debug)] +struct ActionFrontrun { + m_player_targeted: ContractAddress, + m_blockchain_name: ByteArray, + m_value: u8, + m_index: u8 +} + +/// One player pays a gas fee for each blockchain you own in a selected color. +/// OR +/// Every player pays a gas fee for each blockchain you own in either color. +/// +/// Fields: +/// *m_players_affected*: Enum indicating who is the target(s) of this action (owing). +/// *m_blockchain_type_affected*: Enum Specifying what type of gas fee this action is. +/// If the action is targeted at everyone, one of two colors can be used. +/// If the action targets only one opponent, only one color can be used. +/// *m_count*: How the fee will be calculated depnding on how many cards of the same color. +/// *m_value*: Value of the card itself, in case we want to give it as eth. +/// *m_index*: The card index from all of its duplicates in the deck. +#[derive(Drop, Serde, Clone, Introspect, Debug)] +struct ActionGasFee { + m_owner: ContractAddress, + m_players_affected: EnumPlayerTarget, + // First blockchain (target one player), second blockchain (Target all players). + m_blockchain_type_affected: EnumGasFeeType, + m_set_applied: Array, + m_value: u8, + m_index: u8 +} + +/// Useable within 10 seconds of certain Onchain Events - cancels other players Onchain Event card. +/// +/// Fields: +/// *m_owner*: Which player (or dealer) owns this card. +/// *m_timestamp_used: Timestamp in seconds of when the player used this card after the last action +/// played against them. Used to verify that it is within 10 seconds, otherwise penalize the +/// player somehow. +/// *m_value*: Value of the card itself, in case we want to give it as eth. +/// *m_index*: The card index from all of its duplicates in the deck. +#[derive(Drop, Serde, Clone, Introspect, Debug)] +struct ActionHardFork { + m_owner: ContractAddress, + m_timestamp_used: u64, + m_value: u8, + m_index: u8 +} + +/// Add onto any full blockchain set owned to add 3 ETH to value. +/// +/// Fields: +/// *m_set*: Which set to apply this bonus to. +/// *m_value*: Value of the card itself, in case we want to give it as eth. +/// *m_index*: The card index from all of its duplicates in the deck. +#[derive(Drop, Serde, Clone, Introspect, Debug)] +struct ActionMEVBoost { + m_set: StructAssetGroup, + m_value: u8, + m_index: u8 +} + +/// Card that allows a player to draw two additional cards, and make it only count as one move. +/// +/// Fields: +/// *m_value*: Value of the card itself, in case we want to give it as eth. +/// *m_index*: The card index from all of its duplicates in the deck. +#[derive(Drop, Serde, Clone, Introspect, PartialEq, Debug)] +struct ActionPriorityFee { + m_value: u8, + m_index: u8 +} + +/// Played before Gas Fee card, doubles amount of ETH paid to player. +/// +/// Fields: +/// *m_value*: Value of the card itself, in case we want to give it as eth. +/// *m_index*: The card index from all of its duplicates in the deck. +#[derive(Drop, Serde, Clone, Introspect, PartialEq, Debug)] +struct ActionReplayAttack { + m_owner: ContractAddress, + m_value: u8, + m_index: u8 +} + +/// Add onto any full blockchain set owned to add 4 ETH to value. +/// +/// Fields: +/// *m_set*: Which set to apply this bonus to. +/// *m_value*: Value of the card itself, in case we want to give it as eth. +/// *m_index*: The card index from all of its duplicates in the deck. +#[derive(Drop, Serde, Clone, Introspect, Debug)] +struct ActionSoftFork { + m_set: StructAssetGroup, + m_value: u8, + m_index: u8 +} \ No newline at end of file diff --git a/src/models/components.cairo b/src/models/components.cairo index 1ff4aa8..5fcfc07 100644 --- a/src/models/components.cairo +++ b/src/models/components.cairo @@ -10,52 +10,12 @@ use starknet::ContractAddress; use zktt::models::enums::{EnumCard, EnumGameState}; -/////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////// -/////////////////////////////// PARTIALEQ ///////////////////////////////// -/////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////// - -impl HandPartialEq of PartialEq { - fn eq(lhs: @ComponentHand, rhs: @ComponentHand) -> bool { - let mut index: usize = 0; - if lhs.m_cards.len() != rhs.m_cards.len() { - return false; - } - - return loop { - if index >= lhs.m_cards.len() { - break true; - } - - if lhs.m_cards.at(index) != rhs.m_cards.at(index) { - break false; - } - index += 1; - }; - } -} - //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// /////////////////////////////// COMPONENTS ///////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// -/// Component that represents the game state and acts as storage to keep track of the number of -/// players currently at the table. -/// -/// Per table. -#[derive(Drop, Serde, Clone, Debug)] -#[dojo::model] -pub struct ComponentGame { - #[key] - pub m_ent_seed: felt252, - pub m_state: EnumGameState, - pub m_players: Array, - pub m_player_in_turn: ContractAddress -} - /// Component that represents the Pile of cards in the middle of the board, not owned by any player /// yet. /// @@ -100,6 +60,17 @@ pub struct ComponentDeposit { pub m_total_value: u8 } +/// Component that represents the pile of played cards that pile up next to the board deck. +/// +/// Per table. +#[derive(Drop, Serde, Clone, Debug)] +#[dojo::model] +pub struct ComponentDiscardPile { + #[key] + pub m_owner: ContractAddress, + pub m_cards: Array +} + /// Component that represents the cards held in hand of a player in the game. /// /// Per player. @@ -111,6 +82,20 @@ pub struct ComponentHand { pub m_cards: Array } +/// Component that represents the game state and acts as storage to keep track of the number of +/// players currently at the table. +/// +/// Per table. +#[derive(Drop, Serde, Clone, Debug)] +#[dojo::model] +pub struct ComponentGame { + #[key] + pub m_ent_seed: felt252, + pub m_state: EnumGameState, + pub m_players: Array, + pub m_player_in_turn: ContractAddress +} + /// Component that represents a player in the game. Note that the username is not unique, only the /// address is. /// @@ -124,5 +109,6 @@ pub struct ComponentPlayer { pub m_moves_remaining: u8, pub m_score: u32, pub m_has_drawn: bool, + pub m_is_ready: bool, pub m_in_debt: Option } diff --git a/src/models/enums.cairo b/src/models/enums.cairo index 8e467e7..b2c8438 100644 --- a/src/models/enums.cairo +++ b/src/models/enums.cairo @@ -7,15 +7,21 @@ //////////////////////////////// //////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// -use starknet::ContractAddress; -use zktt::models::structs::{ - ActionChainReorg, ActionClaimYield, ActionFrontrun, ActionPriorityFee, ActionReplayAttack, - ActionGasFee, ActionFiftyOnePercentAttack, ActionSandwichAttack, StructAsset, StructBlockchain, +use zktt::models::actions::{ + ActionChainReorg, ActionClaimYield, ActionFrontrun, ActionPriorityFee, + ActionReplayAttack, ActionGasFee, ActionFiftyOnePercentAttack, ActionSandwichAttack, ActionHardFork, + ActionMEVBoost, ActionSoftFork +}; +use zktt::models::traits::{ + ActionFrontrunEq, ActionGasFeeEq, ActionFiftyOnePercentAttackEq, ActionHardForkEq, + ActionMEVBoostEq, ActionSoftForkEq }; +use starknet::ContractAddress; +use zktt::models::structs::{StructAsset, StructBlockchain}; /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// -/////////////////////////////// ENUMS ///////////////////////////////// +/////////////////////////////// CARDS ///////////////////////////////// /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// @@ -26,28 +32,35 @@ pub enum EnumCard { ChainReorg: ActionChainReorg, ClaimYield: ActionClaimYield, GasFee: ActionGasFee, + HardFork: ActionHardFork, + MEVBoost: ActionMEVBoost, PriorityFee: ActionPriorityFee, ReplayAttack: ActionReplayAttack, + SoftFork: ActionSoftFork, FrontRun: ActionFrontrun, FiftyOnePercentAttack: ActionFiftyOnePercentAttack, SandwichAttack: ActionSandwichAttack, } +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +/////////////////////////////// STATES ///////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + #[derive(Drop, Copy, Serde, PartialEq, Introspect, Debug)] pub enum EnumGameState { - WaitingForPlayers: (), - WaitingForRent: (), - Started: (), - Ended: () + WaitingForPlayers, + WaitingForRent, + Started, + Ended } -#[derive(Drop, Copy, Serde, PartialEq, Introspect, Debug)] -pub enum EnumMoveError { - CardAlreadyPresent, - CardNotFound, - NotEnoughMoves, - SetAlreadyPresent -} +/////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// +/////////////////////////////// TYPES ///////////////////////////////// +/////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// #[derive(Drop, Copy, Serde, PartialEq, Introspect, Debug)] pub enum EnumPlayerTarget { @@ -59,11 +72,11 @@ pub enum EnumPlayerTarget { #[derive(Drop, Copy, Serde, PartialEq, Introspect, Debug)] pub enum EnumGasFeeType { Any: (), - AgainstTwo: (EnumBlockchainType, EnumBlockchainType), + AgainstTwo: (EnumColor, EnumColor), } #[derive(Drop, Serde, Copy, PartialEq, Introspect, Debug)] -pub enum EnumBlockchainType { +pub enum EnumColor { Blue, DarkBlue, Gold, @@ -75,3 +88,24 @@ pub enum EnumBlockchainType { Red, Yellow, } + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +/////////////////////////////// ERRORS ///////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +#[derive(Drop, Copy, Serde, PartialEq, Introspect, Debug)] +pub enum EnumMoveError { + CardAlreadyPresent, + CardNotFound, + NotEnoughMoves, + SetAlreadyPresent +} + +#[derive(Drop, Serde, Copy, PartialEq, Debug)] +pub enum EnumHardForkErrors { + InvalidCard, // When card played against is not an onchain event targeting owner of the card. + TooLate // When card is played too late (after 10 secs). +} + diff --git a/src/models/lib.cairo b/src/models/lib.cairo index 2722de3..bcb2fb6 100644 --- a/src/models/lib.cairo +++ b/src/models/lib.cairo @@ -7,7 +7,8 @@ //////////////////////////////// //////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// -mod components; -mod enums; mod structs; +mod actions; mod traits; +mod enums; +mod components; diff --git a/src/models/structs.cairo b/src/models/structs.cairo index 4e7fa61..bb7ce8f 100644 --- a/src/models/structs.cairo +++ b/src/models/structs.cairo @@ -9,9 +9,39 @@ use starknet::ContractAddress; use zktt::models::enums::{ - EnumCard, EnumBlockchainType, EnumGasFeeType, EnumMoveError, EnumPlayerTarget + EnumCard, EnumColor, EnumGasFeeType, EnumMoveError, EnumPlayerTarget }; +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +/////////////////////////////// STRUCTS ///////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +/// Card containing the info about an asset (card that only has monetary value). +#[derive(Drop, Serde, Clone, Introspect, Debug)] +pub struct StructAsset { + m_name: ByteArray, + m_value: u8, + m_index: u8 +} + +/// Card containing the info about a specific asset group (set of matching blockchains). +#[derive(Drop, Serde, Clone, Introspect, Debug)] +pub struct StructAssetGroup { + m_set: Array, + m_total_fee_value: u8 +} + +/// Card containing the info about a specific blockchain. +#[derive(Drop, Serde, Clone, Introspect, Debug)] +pub struct StructBlockchain { + m_name: ByteArray, + m_bc_type: EnumColor, + m_fee: u8, + m_value: u8 +} + /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////// PARTIALEQ ///////////////////////////////// @@ -45,177 +75,3 @@ impl StructBlockchainEq of PartialEq { return lhs.m_name == rhs.m_name; } } - -impl ActionFrontrunEq of PartialEq { - fn eq(lhs: @ActionFrontrun, rhs: @ActionFrontrun) -> bool { - return lhs.m_index == rhs.m_index; - } -} - -impl ActionGasFeeEq of PartialEq { - fn eq(lhs: @ActionGasFee, rhs: @ActionGasFee) -> bool { - return lhs.m_index == rhs.m_index; - } -} - -impl ActionFiftyOnePercentAttackEq of PartialEq { - fn eq(lhs: @ActionFiftyOnePercentAttack, rhs: @ActionFiftyOnePercentAttack) -> bool { - let mut index: usize = 0; - return loop { - if index >= lhs.m_set.len() { - break true; - } - - if lhs.m_set.at(index) != rhs.m_set.at(index) { - break false; - } - index += 1; - }; - } -} - -///////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////// -/////////////////////////////// ACTIONS ///////////////////////////////// -///////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////// - -/// Swap a single blockchain with a player. -/// -/// Fields: -/// *m_self_blockchain_name*: The name of the blockchain from the caller to be swapped. -/// *m_opponent_blockchain_name*: The name of the blockchain to look up from the opponent to be -/// swapped. -/// *m_value*: Value of the card itself, in case we want to give it as eth. -/// *m_index*: The card index from all of its duplicates in the deck. -#[derive(Drop, Serde, Clone, Introspect, PartialEq, Debug)] -struct ActionChainReorg { - m_self_blockchain_name: ByteArray, - m_opponent_blockchain_name: ByteArray, - m_opponent_address: ContractAddress, - m_value: u8, - m_index: u8 -} - -/// All other players pay you 2 ETH. -/// -/// Fields: -/// *m_value*: Value of the card itself, in case we want to give it as eth. -/// *m_index*: The card index from all of its duplicates in the deck. -#[derive(Drop, Serde, Clone, Introspect, PartialEq, Debug)] -struct ActionClaimYield { - m_value: u8, - m_index: u8 -} - -/// All other players pay you 5 ETH. -/// -/// Fields: -/// *m_value*: Value of the card itself, in case we want to give it as eth. -/// *m_index*: The card index from all of its duplicates in the deck. -#[derive(Drop, Serde, Clone, Introspect, PartialEq, Debug)] -struct ActionSandwichAttack { - m_value: u8, - m_index: u8 -} - -/// Card that allows a player to steal a blockchain from another player's deck. -/// -/// Fields: -/// *m_blockchain_name*: Name of the card to be stolen. -/// *m_value*: Value of the card itself, in case we want to give it as eth. -/// *m_index*: The card index from all of its duplicates in the deck. -#[derive(Drop, Serde, Clone, Introspect, Debug)] -struct ActionFrontrun { - m_blockchain_name: ByteArray, - m_value: u8, - m_index: u8 -} - -/// One player pays a gas fee for each blockchain you own in a selected color. -/// OR -/// Every player pays a gas fee for each blockchain you own in either color. -/// -/// Fields: -/// *m_players_affected*: Enum indicating who is the target(s) of this action (owing). -/// *m_blockchain_type_affected*: Enum Specifying what type of gas fee this action is. -/// If the action is targeted at everyone, one of two colors can be used. -/// If the action targets only one opponent, only one color can be used. -/// *m_count*: How the fee will be calculated depnding on how many cards of the same color. -/// *m_value*: Value of the card itself, in case we want to give it as eth. -/// *m_index*: The card index from all of its duplicates in the deck. -#[derive(Drop, Serde, Clone, Introspect, Debug)] -struct ActionGasFee { - m_players_affected: EnumPlayerTarget, - // First blockchain (target one player), second blockchain (Target all players). - m_blockchain_type_affected: EnumGasFeeType, - m_set_applied: Array, - m_value: u8, - m_index: u8 -} - -/// Steal an asset group from an opponent. -/// -/// Fields: -/// *m_full_set*: A array of blockchain names, pointing to which blockchains should this apply for. -/// *m_value*: Value of the card itself, in case we want to give it as eth. -/// *m_index*: The card index from all of its duplicates in the deck. -#[derive(Drop, Serde, Clone, Introspect, Debug)] -struct ActionFiftyOnePercentAttack { - m_owner: ContractAddress, - m_set: Array, - m_value: u8, - m_index: u8 -} - -/// Card that allows a player to draw two additional cards, and make it only count as one move. -/// -/// Fields: -/// *m_value*: Value of the card itself, in case we want to give it as eth. -/// *m_index*: The card index from all of its duplicates in the deck. -#[derive(Drop, Serde, Clone, Introspect, PartialEq, Debug)] -struct ActionPriorityFee { - m_value: u8, - m_index: u8 -} - -/// Played before Gas Fee card, doubles amount of ETH paid to player. -/// -/// Fields: -/// *m_value*: Value of the card itself, in case we want to give it as eth. -/// *m_index*: The card index from all of its duplicates in the deck. -#[derive(Drop, Serde, Clone, Introspect, PartialEq, Debug)] -struct ActionReplayAttack { - m_value: u8, - m_index: u8 -} - -///////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////// -/////////////////////////////// STRUCTS ///////////////////////////////// -///////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////// - -/// Card containing the info about an asset (card that only has monetary value). -#[derive(Drop, Serde, Clone, Introspect, Debug)] -pub struct StructAsset { - m_name: ByteArray, - m_value: u8, - m_index: u8 -} - -/// Card containing the info about a specific asset group (set of matching blockchains). -#[derive(Drop, Serde, Clone, Introspect, Debug)] -pub struct StructAssetGroup { - m_set: Array, - m_total_fee_value: u8 -} - -/// Card containing the info about a specific blockchain. -#[derive(Drop, Serde, Clone, Introspect, Debug)] -pub struct StructBlockchain { - m_name: ByteArray, - m_bc_type: EnumBlockchainType, - m_fee: u8, - m_value: u8 -} diff --git a/src/models/traits.cairo b/src/models/traits.cairo index 0f4d175..22ac9c4 100644 --- a/src/models/traits.cairo +++ b/src/models/traits.cairo @@ -11,24 +11,197 @@ use starknet::ContractAddress; use core::fmt::{Display, Formatter, Error}; use origami_random::deck::{Deck, DeckTrait}; use zktt::models::structs::{ - StructAsset, StructBlockchain, StructAssetGroup, ActionPriorityFee, ActionChainReorg, - ActionSandwichAttack, ActionClaimYield, ActionFrontrun, ActionReplayAttack, ActionGasFee, - ActionFiftyOnePercentAttack + StructAsset, StructBlockchain, StructAssetGroup +}; +use zktt::models::actions::{ + ActionChainReorg, ActionClaimYield, ActionFrontrun, ActionPriorityFee, + ActionReplayAttack, ActionGasFee, ActionFiftyOnePercentAttack, ActionSandwichAttack, + ActionHardFork, ActionMEVBoost, ActionSoftFork }; use zktt::models::enums::{ - EnumCard, EnumBlockchainType, EnumGasFeeType, EnumMoveError, EnumPlayerTarget + EnumCard, EnumColor, EnumGasFeeType, EnumMoveError, EnumPlayerTarget, EnumHardForkErrors }; use zktt::models::components::{ ComponentDealer, ComponentCard, ComponentDeck, ComponentPlayer, ComponentHand, ComponentGame, ComponentDeposit }; + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////// PARTIALEQ ///////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +impl ActionFrontrunEq of PartialEq { + fn eq(lhs: @ActionFrontrun, rhs: @ActionFrontrun) -> bool { + return lhs.m_index == rhs.m_index; + } +} + +impl ActionGasFeeEq of PartialEq { + fn eq(lhs: @ActionGasFee, rhs: @ActionGasFee) -> bool { + return lhs.m_index == rhs.m_index; + } +} + +impl ActionHardForkEq of PartialEq { + fn eq(lhs: @ActionHardFork, rhs: @ActionHardFork) -> bool { + return lhs.m_owner == rhs.m_owner && lhs.m_index == rhs.m_index; + } +} + +impl ActionFiftyOnePercentAttackEq of PartialEq { + fn eq(lhs: @ActionFiftyOnePercentAttack, rhs: @ActionFiftyOnePercentAttack) -> bool { + let mut index: usize = 0; + return loop { + if index >= lhs.m_set.len() { + break true; + } + + if lhs.m_set.at(index) != rhs.m_set.at(index) { + break false; + } + index += 1; + }; + } +} + +impl ActionMEVBoostEq of PartialEq { + fn eq(lhs: @ActionMEVBoost, rhs: @ActionMEVBoost) -> bool { + return lhs.m_index == rhs.m_index; + } +} + +impl ActionSoftForkEq of PartialEq { + fn eq(lhs: @ActionSoftFork, rhs: @ActionSoftFork) -> bool { + return lhs.m_index == rhs.m_index; + } +} + +impl HandPartialEq of PartialEq { + fn eq(lhs: @ComponentHand, rhs: @ComponentHand) -> bool { + let mut index: usize = 0; + if lhs.m_cards.len() != rhs.m_cards.len() { + return false; + } + + return loop { + if index >= lhs.m_cards.len() { + break true; + } + + if lhs.m_cards.at(index) != rhs.m_cards.at(index) { + break false; + } + index += 1; + }; + } +} + ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// /////////////////////////////// DISPLAY ///////////////////////////////// ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// +impl ActionChainReorgDisplay of Display { + fn fmt(self: @ActionChainReorg, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "Chain Reorg: Value {0}, Index {1}", *self.m_value, *self.m_index + ); + f.buffer.append(@str); + return Result::Ok(()); + } +} + +impl ActionClaimYieldDisplay of Display { + fn fmt(self: @ActionClaimYield, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "Claim Yield: Value {0}, Index {1}", *self.m_value, *self.m_index + ); + f.buffer.append(@str); + return Result::Ok(()); + } +} + +impl ActionFrontrunDisplay of Display { + fn fmt(self: @ActionFrontrun, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "Steal Blockchain: Blockchain: {0}, + Value {1}, Index: {2}", + self.m_blockchain_name, + *self.m_value, + *self.m_index + ); + f.buffer.append(@str); + return Result::Ok(()); + } +} + +impl ActionHardForkDisplay of Display { + fn fmt(self: @ActionHardFork, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "Hardfork: Last used: {0}, Value: {1}, Index: {2}", + self.m_timestamp_used, + *self.m_value, + *self.m_index + ); + f.buffer.append(@str); + return Result::Ok(()); + } +} + +impl ActionMEVBoostDisplay of Display { + fn fmt(self: @ActionMEVBoost, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "Add [3] ETH onto asset group: Value: {0}, Index: {1}", *self.m_value, *self.m_index + ); + f.buffer.append(@str); + return Result::Ok(()); + } +} + +impl ActionPriorityFeeDisplay of Display { + fn fmt(self: @ActionPriorityFee, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "Draw Two Cards: Value {0}, Index {1}", *self.m_value, *self.m_index + ); + f.buffer.append(@str); + return Result::Ok(()); + } +} + +impl ActionReplayAttackDisplay of Display { + fn fmt(self: @ActionReplayAttack, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "Replay Attack: Value {0}, Index {1}", *self.m_value, *self.m_index + ); + f.buffer.append(@str); + return Result::Ok(()); + } +} + +impl ActionSandwichAttackDisplay of Display { + fn fmt(self: @ActionSandwichAttack, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "Force any player to give you 5 ETH: Value: {0}, Index: {1}", *self.m_value, *self.m_index + ); + f.buffer.append(@str); + return Result::Ok(()); + } +} + +impl ActionSoftForkDisplay of Display { + fn fmt(self: @ActionSoftFork, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "Add [4] ETH onto asset group: Value: {0}, Index: {1}", *self.m_value, *self.m_index + ); + f.buffer.append(@str); + return Result::Ok(()); + } +} + impl ComponentDeckDisplay of Display { fn fmt(self: @ComponentDeck, ref f: Formatter) -> Result<(), Error> { let str: ByteArray = format!( @@ -104,60 +277,6 @@ impl StructBlockchainDisplay of Display { } } -impl ActionChainReorgDisplay of Display { - fn fmt(self: @ActionChainReorg, ref f: Formatter) -> Result<(), Error> { - let str: ByteArray = format!( - "Chain Reorg: Value {0}, Index {1}", *self.m_value, *self.m_index - ); - f.buffer.append(@str); - return Result::Ok(()); - } -} - -impl ActionClaimYieldDisplay of Display { - fn fmt(self: @ActionClaimYield, ref f: Formatter) -> Result<(), Error> { - let str: ByteArray = format!( - "Claim Yield: Value {0}, Index {1}", *self.m_value, *self.m_index - ); - f.buffer.append(@str); - return Result::Ok(()); - } -} - -impl ActionFrontrunDisplay of Display { - fn fmt(self: @ActionFrontrun, ref f: Formatter) -> Result<(), Error> { - let str: ByteArray = format!( - "Steal Blockchain: Blockchain: {0}, - Value {1}, Index: {2}", - self.m_blockchain_name, - *self.m_value, - *self.m_index - ); - f.buffer.append(@str); - return Result::Ok(()); - } -} - -impl ActionPriorityFeeDisplay of Display { - fn fmt(self: @ActionPriorityFee, ref f: Formatter) -> Result<(), Error> { - let str: ByteArray = format!( - "Draw Two Cards: Value {0}, Index {1}", *self.m_value, *self.m_index - ); - f.buffer.append(@str); - return Result::Ok(()); - } -} - -impl ActionReplayAttackDisplay of Display { - fn fmt(self: @ActionReplayAttack, ref f: Formatter) -> Result<(), Error> { - let str: ByteArray = format!( - "Replay Attack: Value {0}, Index {1}", *self.m_value, *self.m_index - ); - f.buffer.append(@str); - return Result::Ok(()); - } -} - impl EnumCardDisplay of Display { fn fmt(self: @EnumCard, ref f: Formatter) -> Result<(), Error> { match self { @@ -191,46 +310,46 @@ impl EnumCardDisplay of Display { } } -impl EnumBlockchainTypeDisplay of Display { - fn fmt(self: @EnumBlockchainType, ref f: Formatter) -> Result<(), Error> { +impl EnumColorDisplay of Display { + fn fmt(self: @EnumColor, ref f: Formatter) -> Result<(), Error> { match self { - EnumBlockchainType::Blue(_) => { + EnumColor::Blue(_) => { let str: ByteArray = format!("Blue"); f.buffer.append(@str); }, - EnumBlockchainType::DarkBlue(_) => { + EnumColor::DarkBlue(_) => { let str: ByteArray = format!("Dark Blue"); f.buffer.append(@str); }, - EnumBlockchainType::Gold(_) => { + EnumColor::Gold(_) => { let str: ByteArray = format!("Gold"); f.buffer.append(@str); }, - EnumBlockchainType::Green(_) => { + EnumColor::Green(_) => { let str: ByteArray = format!("Green"); f.buffer.append(@str); }, - EnumBlockchainType::Grey(_) => { + EnumColor::Grey(_) => { let str: ByteArray = format!("Grey"); f.buffer.append(@str); }, - EnumBlockchainType::LightBlue(_) => { + EnumColor::LightBlue(_) => { let str: ByteArray = format!("Light Blue"); f.buffer.append(@str); }, - EnumBlockchainType::Pink(_) => { + EnumColor::Pink(_) => { let str: ByteArray = format!("Pink"); f.buffer.append(@str); }, - EnumBlockchainType::Purple(_) => { + EnumColor::Purple(_) => { let str: ByteArray = format!("Purple"); f.buffer.append(@str); }, - EnumBlockchainType::Red(_) => { + EnumColor::Red(_) => { let str: ByteArray = format!("Red"); f.buffer.append(@str); }, - EnumBlockchainType::Yellow(_) => { + EnumColor::Yellow(_) => { let str: ByteArray = format!("Yellow"); f.buffer.append(@str); }, @@ -324,8 +443,11 @@ impl EnumCardInto of Into<@EnumCard, ByteArray> { EnumCard::ChainReorg(_) => "Chain Reorg", EnumCard::ClaimYield(_) => "Claim Yield", EnumCard::GasFee(_) => "Gas Fee", + EnumCard::HardFork(_) => "HardFork", + EnumCard::MEVBoost(_) => "MEV Boost", EnumCard::PriorityFee(_) => "Priority Fee", EnumCard::ReplayAttack(_) => "Replay Attack", + EnumCard::SoftFork(_) => "Soft Fork", EnumCard::FrontRun(_) => "Frontrun", EnumCard::FiftyOnePercentAttack(_) => "51% Attack", EnumCard::SandwichAttack(_) => "Sandwich Attack" @@ -336,37 +458,86 @@ impl EnumCardInto of Into<@EnumCard, ByteArray> { //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// -/////////////////////////////// TRAITS ///////////////////////////////// +/////////////////////////////// IMPLS ////////////////////////////////// //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// #[generate_trait] -impl StructAssetImpl of IAsset { +impl AssetImpl of IAsset { fn new(name: ByteArray, value: u8, copies_left: u8) -> StructAsset nopanic { return StructAsset { m_name: name, m_value: value, m_index: copies_left }; } } #[generate_trait] -impl StructAssetGroupImpl of IAssetGroup { +impl AssetGroupImpl of IAssetGroup { + fn default() -> StructAssetGroup nopanic { + return StructAssetGroup { + m_set: array![], + m_total_fee_value: 0 + }; + } + fn new(blockchains: Array, total_fee_value: u8) -> StructAssetGroup nopanic { return StructAssetGroup { m_set: blockchains, m_total_fee_value: total_fee_value }; } } #[generate_trait] -impl StructBlockchainImpl of IBlockchain { +impl BlockchainImpl of IBlockchain { fn new( - name: ByteArray, bc_type: EnumBlockchainType, fee: u8, value: u8 + name: ByteArray, bc_type: EnumColor, fee: u8, value: u8 ) -> StructBlockchain nopanic { return StructBlockchain { m_name: name, m_bc_type: bc_type, m_fee: fee, m_value: value }; } } #[generate_trait] -impl StructPriorityFeeImpl of IDraw { - fn new(value: u8, copies_left: u8) -> ActionPriorityFee nopanic { - return ActionPriorityFee { m_value: value, m_index: copies_left }; +impl ChainReorgImpl of IChainReorg { + fn default() -> ActionChainReorg nopanic { + return ActionChainReorg { + m_self_blockchain_name: "", + m_opponent_blockchain_name: "", + m_opponent_address: starknet::contract_address_const::<0x0>(), + m_value: 3, + m_index: 3 + }; + } + + fn new( + self_blockchain_name: ByteArray, + opponent_blockchain_name: ByteArray, + opponent_address: ContractAddress, + value: u8, + copies_left: u8 + ) -> ActionChainReorg nopanic { + return ActionChainReorg { + m_self_blockchain_name: self_blockchain_name, + m_opponent_blockchain_name: opponent_blockchain_name, + m_opponent_address: opponent_address, + m_value: value, + m_index: copies_left + }; + } +} + +#[generate_trait] +impl ClaimYieldImpl of IClaimYield { + fn default() -> ActionClaimYield nopanic { + return ActionClaimYield { + m_value: 2, + m_index: 3 + }; + } + fn new(value: u8, copies_left: u8) -> ActionClaimYield nopanic { + return ActionClaimYield { m_value: value, m_index: copies_left }; + } +} + +#[generate_trait] +impl ComponentCardImpl of ICard { + fn new(index: u32, card: EnumCard) -> ComponentCard nopanic { + return ComponentCard { m_ent_index: index, m_card_info: card }; } } @@ -457,7 +628,7 @@ impl DeckImpl of IDeck { return found; } - fn contains_type(self: @ComponentDeck, bc_type: @EnumBlockchainType) -> Option { + fn contains_type(self: @ComponentDeck, bc_type: @EnumColor) -> Option { let mut index = 0; let mut found = Option::None; @@ -560,12 +731,12 @@ impl DeckImpl of IDeck { fn check_complete_set( self: @ComponentDeck, asset_group_array: Array, - bc_type: @EnumBlockchainType + bc_type: @EnumColor ) -> bool { let required_count = match bc_type { - EnumBlockchainType::Blue(_) | EnumBlockchainType::DarkBlue(_) | - EnumBlockchainType::Gold(_) => 2, - EnumBlockchainType::LightBlue(_) => 4, + EnumColor::Blue(_) | EnumColor::DarkBlue(_) | + EnumColor::Gold(_) => 2, + EnumColor::LightBlue(_) => 4, _ => 3 }; @@ -574,19 +745,72 @@ impl DeckImpl of IDeck { } #[generate_trait] -impl EnumBlockchainTypeImpl of IEnumBlockchainType { - fn get_boost_array(self: @EnumBlockchainType) -> Array { +impl DepositImpl of IDeposit { + fn new(owner: ContractAddress, cards: Array, value: u8) -> ComponentDeposit { + return ComponentDeposit { m_ent_owner: owner, m_cards: cards, m_total_value: value }; + } + + fn add(ref self: ComponentDeposit, mut card: EnumCard) -> () { + assert!(!card.is_blockchain(), "Blockchains cannot be added to money pile"); + self.m_total_value += card.get_value(); + self.m_cards.append(card); + return (); + } + + fn contains(self: @ComponentDeposit, card_name: @ByteArray) -> Option { + let mut index: usize = 0; + + return loop { + if index >= self.m_cards.len() { + break Option::None; + } + + if @self.m_cards.at(index).get_name() == card_name { + break Option::Some(index); + } + + index += 1; + }; + } + + fn remove(ref self: ComponentDeposit, card_name: @ByteArray) -> () { + if let Option::Some(index_found) = self.contains(card_name) { + if self.m_total_value < self.m_cards.at(index_found).get_value() { + self.m_total_value = 0; + } else { + self.m_total_value -= self.m_cards.at(index_found).get_value(); + } + let mut new_array: Array = ArrayTrait::new(); + let mut index: usize = 0; + while let Option::Some(card) = self.m_cards.pop_front() { + if index == index_found { + index += 1; + continue; + } + + new_array.append(card); + index += 1; + }; + self.m_cards = new_array; + } + return (); + } +} + +#[generate_trait] +impl EnumColorImpl of IEnumColor { + fn get_boost_array(self: @EnumColor) -> Array { return match self { - EnumBlockchainType::Blue => { return array![1, 2]; }, - EnumBlockchainType::DarkBlue => { return array![3, 8]; }, - EnumBlockchainType::Gold => { return array![1, 2]; }, - EnumBlockchainType::Green => { return array![1, 3, 5]; }, - EnumBlockchainType::Grey => { return array![1, 2, 4]; }, - EnumBlockchainType::LightBlue => { return array![1, 2, 3, 4]; }, - EnumBlockchainType::Pink => { return array![1, 2, 3]; }, - EnumBlockchainType::Purple => { return array![2, 4, 6]; }, - EnumBlockchainType::Red => { return array![2, 4, 7]; }, - EnumBlockchainType::Yellow => { return array![2, 3, 6]; }, + EnumColor::Blue => { return array![1, 2]; }, + EnumColor::DarkBlue => { return array![3, 8]; }, + EnumColor::Gold => { return array![1, 2]; }, + EnumColor::Green => { return array![1, 3, 5]; }, + EnumColor::Grey => { return array![1, 2, 4]; }, + EnumColor::LightBlue => { return array![1, 2, 3, 4]; }, + EnumColor::Pink => { return array![1, 2, 3]; }, + EnumColor::Purple => { return array![2, 4, 6]; }, + EnumColor::Red => { return array![2, 4, 7]; }, + EnumColor::Yellow => { return array![2, 3, 6]; }, }; } } @@ -609,12 +833,15 @@ impl EnumCardImpl of IEnumCard { EnumCard::Blockchain(_data) => { return 1; }, EnumCard::ChainReorg(data) => { return *data.m_index; }, EnumCard::ClaimYield(data) => { return *data.m_index; }, + EnumCard::FiftyOnePercentAttack(_) => { return 1; }, + EnumCard::FrontRun(data) => { return *data.m_index; }, EnumCard::GasFee(data) => { return *data.m_index; }, + EnumCard::HardFork(data) => { return *data.m_index; }, + EnumCard::MEVBoost(data) => { return *data.m_index; }, EnumCard::PriorityFee(data) => { return *data.m_index; }, EnumCard::ReplayAttack(data) => { return *data.m_index; }, - EnumCard::FrontRun(data) => { return *data.m_index; }, - EnumCard::FiftyOnePercentAttack(_) => { return 1; }, - EnumCard::SandwichAttack(data) => { return *data.m_index; } + EnumCard::SandwichAttack(data) => { return *data.m_index; }, + EnumCard::SoftFork(data) => { return *data.m_index; }, }; } @@ -628,12 +855,15 @@ impl EnumCardImpl of IEnumCard { EnumCard::Blockchain(data) => { return *data.m_value; }, EnumCard::ChainReorg(data) => { return *data.m_value; }, EnumCard::ClaimYield(data) => { return *data.m_value; }, + EnumCard::FiftyOnePercentAttack(data) => { return *data.m_value; }, + EnumCard::FrontRun(data) => { return *data.m_value; }, EnumCard::GasFee(data) => { return *data.m_value; }, + EnumCard::HardFork(data) => { return *data.m_value; }, + EnumCard::MEVBoost(data) => { return *data.m_value; }, EnumCard::PriorityFee(data) => { return *data.m_value; }, EnumCard::ReplayAttack(data) => { return *data.m_value; }, - EnumCard::FrontRun(data) => { return *data.m_value; }, - EnumCard::FiftyOnePercentAttack(data) => { return *data.m_value; }, - EnumCard::SandwichAttack(data) => { return *data.m_value; } + EnumCard::SandwichAttack(data) => { return *data.m_value; }, + EnumCard::SoftFork(data) => { return *data.m_value; }, }; } @@ -654,6 +884,16 @@ impl EnumCardImpl of IEnumCard { data.m_index -= 1; return EnumCard::ClaimYield(data); }, + EnumCard::FiftyOnePercentAttack(mut data) => { + assert!(data.m_index > 0, "No more indices left for {0}", data); + data.m_index -= 1; + return EnumCard::FiftyOnePercentAttack(data); + }, + EnumCard::FrontRun(mut data) => { + assert!(data.m_index > 0, "No more indices left for {0}", data); + data.m_index -= 1; + return EnumCard::FrontRun(data); + }, EnumCard::GasFee(mut data) => { assert!(data.m_index > 0, "No more indices left for {0}", data); data.m_index -= 1; @@ -669,15 +909,15 @@ impl EnumCardImpl of IEnumCard { data.m_index -= 1; return EnumCard::ReplayAttack(data); }, - EnumCard::FrontRun(mut data) => { + EnumCard::SandwichAttack(mut data) => { assert!(data.m_index > 0, "No more indices left for {0}", data); data.m_index -= 1; - return EnumCard::FrontRun(data); + return EnumCard::SandwichAttack(data); }, - EnumCard::FiftyOnePercentAttack(mut data) => { + EnumCard::SoftFork(mut data) => { assert!(data.m_index > 0, "No more indices left for {0}", data); data.m_index -= 1; - return EnumCard::FiftyOnePercentAttack(data); + return EnumCard::SoftFork(data); }, _ => { return self.clone(); } }; @@ -698,6 +938,49 @@ impl EnumCardImpl of IEnumCard { } } +#[generate_trait] +impl FiftyOnePercentAttackImpl of IFiftyOnePercentAttack { + fn default() -> ActionFiftyOnePercentAttack nopanic { + return ActionFiftyOnePercentAttack { + m_player_targeted: starknet::contract_address_const::<0x0>(), + m_set: array![], + m_value: 5, + m_index: 1 + }; + } + + fn new(player_targeted: ContractAddress, set: Array, value: u8, copies_left: u8 + ) -> ActionFiftyOnePercentAttack nopanic { + return ActionFiftyOnePercentAttack { + m_player_targeted: player_targeted, + m_set: set, + m_value: value, + m_index: copies_left + }; + } +} + +#[generate_trait] +impl FrontRunImpl of IFrontRun { + fn default() -> ActionFrontrun nopanic { + return ActionFrontrun { + m_player_targeted: starknet::contract_address_const::<0x0>(), + m_blockchain_name: "", + m_value: 3, + m_index: 3 + }; + } + + fn new(player_targeted: ContractAddress, blockchain_name: ByteArray, value: u8, copies_left: u8) -> ActionFrontrun nopanic { + return ActionFrontrun { + m_player_targeted: player_targeted, + m_blockchain_name: blockchain_name, + m_value: value, + m_index: copies_left + }; + } +} + #[generate_trait] impl GameImpl of IGame { fn add_player(ref self: ComponentGame, mut new_player: ContractAddress) -> () { @@ -768,6 +1051,7 @@ impl GasFeeImpl of IGasFee { copies_left: u8 ) -> ActionGasFee nopanic { return ActionGasFee { + m_owner: starknet::contract_address_const::<0x0>(), m_players_affected: players_affected, m_set_applied: set_applied, m_blockchain_type_affected: bc_affected, @@ -781,72 +1065,6 @@ impl GasFeeImpl of IGasFee { } } -#[generate_trait] -impl ClaimYieldImpl of IClaimYield { - fn new(value: u8, copies_left: u8) -> ActionClaimYield nopanic { - return ActionClaimYield { m_value: value, m_index: copies_left }; - } -} - -#[generate_trait] -impl SandwichAttackImpl of ISandwichAttack { - fn new(value: u8, copies_left: u8) -> ActionSandwichAttack nopanic { - return ActionSandwichAttack { m_value: value, m_index: copies_left }; - } -} - -#[generate_trait] -impl FiftyOnePercentAttackImpl of IFiftyOnePercentAttack { - fn new( - owner: ContractAddress, set: Array, value: u8, copies_left: u8 - ) -> ActionFiftyOnePercentAttack nopanic { - return ActionFiftyOnePercentAttack { - m_owner: owner, m_set: set, m_value: value, m_index: copies_left - }; - } -} - -#[generate_trait] -impl PriorityFeeImpl of IPriorityFee { - fn new(value: u8, copies_left: u8) -> ActionPriorityFee nopanic { - return ActionPriorityFee { m_value: value, m_index: copies_left }; - } -} - -#[generate_trait] -impl FrontRunImpl of IFrontRun { - fn new(blockchain_name: ByteArray, value: u8, copies_left: u8) -> ActionFrontrun nopanic { - return ActionFrontrun { - m_blockchain_name: blockchain_name, m_value: value, m_index: copies_left - }; - } -} - -#[generate_trait] -impl ComponentCardImpl of ICard { - fn new(index: u32, card: EnumCard) -> ComponentCard nopanic { - return ComponentCard { m_ent_index: index, m_card_info: card }; - } -} - -#[generate_trait] -impl ChainReorgImpl of IChainReorg { - fn new( - self_blockchain_name: ByteArray, - opponent_blockchain_name: ByteArray, - opponent_address: ContractAddress, - value: u8, - copies_left: u8 - ) -> ActionChainReorg nopanic { - return ActionChainReorg { - m_self_blockchain_name: self_blockchain_name, - m_opponent_blockchain_name: opponent_blockchain_name, - m_opponent_address: opponent_address, - m_value: value, - m_index: copies_left - }; - } -} #[generate_trait] impl HandImpl of IHand { @@ -895,55 +1113,60 @@ impl HandImpl of IHand { } #[generate_trait] -impl DepositImpl of IDeposit { - fn new(owner: ContractAddress, cards: Array, value: u8) -> ComponentDeposit { - return ComponentDeposit { m_ent_owner: owner, m_cards: cards, m_total_value: value }; +impl HardForkImpl of IHardFork { + fn default() -> ActionHardFork nopanic { + return ActionHardFork { + m_owner: starknet::contract_address_const::<0x0>(), + m_timestamp_used: 0, + m_value: 3, + m_index: 3 + }; } - fn add(ref self: ComponentDeposit, mut card: EnumCard) -> () { - assert!(!card.is_blockchain(), "Blockchains cannot be added to money pile"); - self.m_total_value += card.get_value(); - self.m_cards.append(card); - return (); + fn new(owner: ContractAddress, timestamp_used: u64, value: u8, copies: u8) -> ActionHardFork nopanic { + return ActionHardFork { + m_owner: owner, + m_timestamp_used: timestamp_used, + m_value: value, + m_index: copies + }; } - fn contains(self: @ComponentDeposit, card_name: @ByteArray) -> Option { - let mut index: usize = 0; - - return loop { - if index >= self.m_cards.len() { - break Option::None; - } - - if @self.m_cards.at(index).get_name() == card_name { - break Option::Some(index); - } - - index += 1; + fn is_cancelable(self: @ActionHardFork, card: @EnumCard) -> bool { + return match card { + EnumCard::ClaimYield(_) => true, + EnumCard::FrontRun(frontrun_struct) => { + return frontrun_struct.m_player_targeted == self.m_owner; + }, + EnumCard::GasFee(_) => true, + EnumCard::HardFork(_) => true, + EnumCard::SandwichAttack(sandwich_struct) => { + return sandwich_struct.m_player_targeted == self.m_owner; + }, + EnumCard::FiftyOnePercentAttack(fifty_percent_attack_struct) => { + return fifty_percent_attack_struct.m_player_targeted == self.m_owner; + }, + _ => false }; } +} - fn remove(ref self: ComponentDeposit, card_name: @ByteArray) -> () { - if let Option::Some(index_found) = self.contains(card_name) { - if self.m_total_value < self.m_cards.at(index_found).get_value() { - self.m_total_value = 0; - } else { - self.m_total_value -= self.m_cards.at(index_found).get_value(); - } - let mut new_array: Array = ArrayTrait::new(); - let mut index: usize = 0; - while let Option::Some(card) = self.m_cards.pop_front() { - if index == index_found { - index += 1; - continue; - } +#[generate_trait] +impl MEVBoostImpl of IMEVBoost { + fn default() -> ActionMEVBoost nopanic { + return ActionMEVBoost { + m_set: IAssetGroup::default(), + m_value: 4, + m_index: 3 + }; + } - new_array.append(card); - index += 1; - }; - self.m_cards = new_array; - } - return (); + fn new(set: StructAssetGroup, value: u8, copies_left: u8) -> ActionMEVBoost nopanic { + return ActionMEVBoost { + m_set: set, + m_value: value, + m_index: copies_left + }; } } @@ -956,6 +1179,7 @@ impl PlayerImpl of IPlayer { m_moves_remaining: 3, m_score: 0, m_has_drawn: false, + m_is_ready: false, m_in_debt: Option::None }; } @@ -964,3 +1188,74 @@ impl PlayerImpl of IPlayer { return self.m_in_debt.clone(); } } + +#[generate_trait] +impl PriorityFeeImpl of IPriorityFee { + fn default() -> ActionPriorityFee nopanic { + return ActionPriorityFee { + m_value: 1, + m_index: 10 + }; + } + + fn new(value: u8, copies_left: u8) -> ActionPriorityFee nopanic { + return ActionPriorityFee { m_value: value, m_index: copies_left }; + } +} + +#[generate_trait] +impl ReplayAttackImpl of IReplayAttack { + fn default() -> ActionReplayAttack nopanic { + return ActionReplayAttack { + m_owner: starknet::contract_address_const::<0x0>(), + m_value: 1, + m_index: 2 + }; + } + + fn new(owner: ContractAddress, value: u8, copies: u8) -> ActionReplayAttack nopanic { + return ActionReplayAttack { + m_owner: owner, + m_value: value, + m_index: copies + }; + } +} + +#[generate_trait] +impl SandwichAttackImpl of ISandwichAttack { + fn default() -> ActionSandwichAttack nopanic { + return ActionSandwichAttack { + m_player_targeted: starknet::contract_address_const::<0x0>(), + m_value: 3, + m_index: 3 + }; + } + + fn new(player_targeted: ContractAddress, value: u8, copies_left: u8) -> ActionSandwichAttack nopanic { + return ActionSandwichAttack { + m_player_targeted: player_targeted, + m_value: value, + m_index: copies_left + }; + } +} + +#[generate_trait] +impl SoftForkImpl of ISoftFork { + fn default() -> ActionSoftFork nopanic { + return ActionSoftFork { + m_set: IAssetGroup::default(), + m_value: 3, + m_index: 3 + }; + } + + fn new(set: StructAssetGroup, value: u8, copies_left: u8) -> ActionSoftFork nopanic { + return ActionSoftFork { + m_set: set, + m_value: value, + m_index: copies_left + }; + } +} From a618124f16957a536342127040a74671a74d85f2 Mon Sep 17 00:00:00 2001 From: Nami Reghbati Date: Wed, 27 Nov 2024 15:49:11 -0500 Subject: [PATCH 02/15] chore: Reamped general structure & updated impl for cards. --- src/lib.cairo | 5 +- src/systems/actions.cairo | 231 ++++++++++++++++++++++---------------- src/systems/game.cairo | 212 +++++++++++++++++----------------- src/systems/player.cairo | 141 +++++++++++++++++++---- 4 files changed, 368 insertions(+), 221 deletions(-) diff --git a/src/lib.cairo b/src/lib.cairo index d5b6783..b6fdb18 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -8,10 +8,11 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// mod models { - mod components; - mod enums; mod structs; + mod actions; mod traits; + mod enums; + mod components; } mod systems { diff --git a/src/systems/actions.cairo b/src/systems/actions.cairo index 45a5d90..4fdaf5a 100644 --- a/src/systems/actions.cairo +++ b/src/systems/actions.cairo @@ -9,17 +9,18 @@ use starknet::ContractAddress; use zktt::models::enums::{ - EnumCard, EnumGameState, EnumGasFeeType, EnumPlayerTarget, EnumBlockchainType + EnumCard, EnumGameState, EnumGasFeeType, EnumPlayerTarget, EnumColor }; #[starknet::interface] trait IActionSystem { - fn draw(ref self: T, draws_five: bool) -> (); - fn play(ref self: T, card: EnumCard) -> (); - fn move(ref self: T, card: EnumCard) -> (); + fn draw(ref self: T, draws_five: bool, table: ContractAddress) -> (); + fn play(ref self: T, card: EnumCard, table: ContractAddress) -> (); + fn move(ref self: T, card: EnumCard, table: ContractAddress) -> (); fn pay_fee( - ref self: T, pay: Array, recipient: ContractAddress, payee: ContractAddress + ref self: T, pay: Array, recipient: ContractAddress, payee: ContractAddress, + table: ContractAddress ) -> (); } @@ -28,13 +29,13 @@ mod action_system { use super::{EnumCard, EnumGameState, ContractAddress}; use zktt::models::components::{ ComponentGame, ComponentCard, ComponentHand, ComponentDeck, ComponentDeposit, - ComponentPlayer, ComponentDealer + ComponentPlayer, ComponentDealer, ComponentDiscardPile }; use zktt::models::traits::{ - IEnumCard, IPlayer, IDeck, IDealer, IHand, IGasFee, IAssetGroup, IDraw, IGame, IAsset, + IEnumCard, IPlayer, IDeck, IDealer, IHand, IGasFee, IAssetGroup, IGame, IAsset, IBlockchain, IDeposit }; - use zktt::models::enums::{EnumGasFeeType, EnumPlayerTarget, EnumBlockchainType}; + use zktt::models::enums::{EnumGasFeeType, EnumPlayerTarget, EnumColor}; use dojo::world::IWorldDispatcher; use starknet::get_caller_address; use dojo::model::ModelStorage; @@ -60,19 +61,19 @@ mod action_system { /// Output: /// None. /// Can Panic?: yes - fn draw(ref self: ContractState, draws_five: bool) -> () { + fn draw(ref self: ContractState, draws_five: bool, table: ContractAddress) -> () { let mut world = self.world_default(); let caller = get_caller_address(); let mut hand: ComponentHand = world.read_model(caller); let mut player: ComponentPlayer = world.read_model(caller); - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(table); assert!(game.m_state != EnumGameState::WaitingForRent, "Game is paused"); assert!(game.m_state == EnumGameState::Started, "Game has not started yet"); assert!(game.m_player_in_turn == caller, "Not player's turn"); assert!(!player.m_has_drawn, "Cannot draw mid-turn"); - let mut dealer: ComponentDealer = world.read_model(world.dispatcher.contract_address); + let mut dealer: ComponentDealer = world.read_model(table); if draws_five { assert!(hand.m_cards.len() == 0, "Cannot draw five, hand not empty"); @@ -118,9 +119,9 @@ mod action_system { /// Output: /// None. /// Can Panic?: yes - fn play(ref self: ContractState, card: EnumCard) -> () { + fn play(ref self: ContractState, card: EnumCard, table: ContractAddress) -> () { let mut world = self.world_default(); - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(table); assert!(game.m_state != EnumGameState::WaitingForRent, "Game is paused"); assert!(game.m_state == EnumGameState::Started, "Game has not started yet"); @@ -132,7 +133,7 @@ mod action_system { assert!(player.m_moves_remaining != 0, "No moves left"); assert!(self._is_owner(@card.get_name(), @caller), "Player does not own card"); - self._use_card(@caller, card); + self._use_card(@caller, card, table); player.m_moves_remaining -= 1; world.write_model(@player); } @@ -150,9 +151,9 @@ mod action_system { /// Output: /// None. /// Can Panic?: yes - fn move(ref self: ContractState, card: EnumCard) -> () { + fn move(ref self: ContractState, card: EnumCard, table: ContractAddress) -> () { let mut world = self.world_default(); - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(table); assert!(game.m_state != EnumGameState::WaitingForRent, "Game is paused"); assert!(game.m_state == EnumGameState::Started, "Game has not started yet"); @@ -185,10 +186,11 @@ mod action_system { ref self: ContractState, mut pay: Array, recipient: ContractAddress, - payee: ContractAddress + payee: ContractAddress, + table: ContractAddress ) -> () { let mut world = self.world_default(); - let mut game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let mut game: ComponentGame = world.read_model(table); assert!(game.m_state == EnumGameState::WaitingForRent, "Game must be waiting for rent"); let mut player: ComponentPlayer = world.read_model(payee); @@ -266,11 +268,12 @@ mod action_system { /// Output: /// None. /// Can Panic?: yes - fn _use_card(ref self: ContractState, caller: @ContractAddress, card: EnumCard) -> () { + fn _use_card(ref self: ContractState, caller: @ContractAddress, card: EnumCard, table: ContractAddress) -> () { let mut world = self.world_default(); let mut hand: ComponentHand = world.read_model(*caller); let mut deck: ComponentDeck = world.read_model(*caller); let mut deposit: ComponentDeposit = world.read_model(*caller); + let mut discard_pile: ComponentDiscardPile = world.read_model(table); assert!(hand.contains(@card.get_name()).is_some(), "Card not in player's hand"); hand.remove(@card.get_name()); @@ -283,7 +286,6 @@ mod action_system { deck.add(EnumCard::Blockchain(blockchain_struct.clone())); world.write_model(@deck); }, - // ... existing code ... EnumCard::ChainReorg(chain_reorg_struct) => { let mut opponent_deck: ComponentDeck = world .read_model(*chain_reorg_struct.m_opponent_address); @@ -310,30 +312,68 @@ mod action_system { } }, EnumCard::ClaimYield(_claim_yield_struct) => { - let mut game: ComponentGame = world - .read_model(world.dispatcher.contract_address); + let mut game: ComponentGame = world.read_model(table); game.m_state = EnumGameState::WaitingForRent; - for player in game - .m_players - .span() { - let mut player_component: ComponentPlayer = world.read_model(*player); - player_component.m_in_debt = Option::Some(2); - world.write_model(@player_component); - }; + for player in game.m_players.span() { + let mut player_component: ComponentPlayer = world.read_model(*player); + player_component.m_in_debt = Option::Some(2); + world.write_model(@player_component); + }; world.write_model(@game); }, - EnumCard::SandwichAttack(_sandwich_attack_struct) => { - let mut game: ComponentGame = world - .read_model(world.dispatcher.contract_address); - game.m_state = EnumGameState::WaitingForRent; - for player in game - .m_players - .span() { - let mut player_component: ComponentPlayer = world.read_model(*player); - player_component.m_in_debt = Option::Some(5); - world.write_model(@player_component); - }; - world.write_model(@game); + EnumCard::FiftyOnePercentAttack(asset_group_struct) => { + assert!(*asset_group_struct.m_player_targeted != starknet::contract_address_const::<0x0>(), + "No player targeted"); + let mut opponent_deck: ComponentDeck = world.read_model(*asset_group_struct.m_player_targeted); + + // Verify opponent has sets to steal + assert!(opponent_deck.m_sets > 0, "Opponent has no sets"); + + // Get all matching blockchains of the target type + let mut index: usize = 0; + let target_type = asset_group_struct.m_set.at(0).m_bc_type; + let mut blockchains_to_steal = ArrayTrait::new(); + + // First collect all matching blockchains + while index < opponent_deck.m_cards.len() { + let bc = opponent_deck.m_cards.at(index); + match bc { + EnumCard::Blockchain(bc_struct) => { + if bc_struct.m_bc_type == target_type { + blockchains_to_steal.append(bc.clone()); + } + }, + _ => {} + } + index += 1; + }; + + // Then move all collected blockchains from opponent to self + while let Option::Some(bc) = blockchains_to_steal.pop_front() { + deck.add(bc.clone()); + opponent_deck.remove(@bc.get_name()); + }; + + world.write_model(@deck); + world.write_model(@opponent_deck); + }, + EnumCard::FrontRun(frontrun_struct) => { + assert!(*frontrun_struct.m_player_targeted != starknet::contract_address_const::<0x0>(), + "No player targeted"); + let bc_owner = self._get_owner(frontrun_struct.m_blockchain_name, table); + assert!(bc_owner.is_some(), "Blockchain in Frontrun card has no owner"); + + let mut opponent_deck: ComponentDeck = world.read_model(*frontrun_struct.m_player_targeted); + if let Option::Some(card_index) = opponent_deck + .contains(frontrun_struct.m_blockchain_name) { + deck.add(opponent_deck.m_cards.at(card_index).clone()); + opponent_deck.remove(frontrun_struct.m_blockchain_name); + world.write_model(@deck); + world.write_model(@deck); + world.write_model(@opponent_deck); + } else { + panic!("Invalid Frontrun move: Opponent blockchain not found"); + } }, EnumCard::GasFee(gas_fee_struct) => { match gas_fee_struct.m_blockchain_type_affected { @@ -358,8 +398,7 @@ mod action_system { }; let fee: u8 = gas_fee_struct.get_fee(); - let mut game: ComponentGame = world - .read_model(world.dispatcher.contract_address); + let mut game: ComponentGame = world.read_model(table); game.m_state = EnumGameState::WaitingForRent; // Make every affected player in debt for their next turn. match gas_fee_struct.m_players_affected { @@ -384,8 +423,7 @@ mod action_system { world.write_model(@game); }, EnumCard::PriorityFee(_priority_fee_struct) => { - let mut dealer: ComponentDealer = world - .read_model(world.dispatcher.contract_address); + let mut dealer: ComponentDealer = world.read_model(table); assert!(!dealer.m_cards.is_empty(), "Dealer has no more cards"); let card_component1: ComponentCard = world @@ -397,67 +435,68 @@ mod action_system { world.write_model(@hand); world.write_model(@dealer); }, - //EnumCard::ReplayAttack(_replay_attack_struct) => {}, - EnumCard::FrontRun(frontrun_struct) => { - let bc_owner = self._get_owner(frontrun_struct.m_blockchain_name); - assert!(bc_owner.is_some(), "Blockchain in Frontrun card has no owner"); - - let mut opponent_deck: ComponentDeck = world.read_model(bc_owner.unwrap()); - if let Option::Some(card_index) = opponent_deck - .contains(frontrun_struct.m_blockchain_name) { - deck.add(opponent_deck.m_cards.at(card_index).clone()); - opponent_deck.remove(frontrun_struct.m_blockchain_name); - world.write_model(@deck); - world.write_model(@deck); - world.write_model(@opponent_deck); - } else { - panic!("Invalid FrontRun move: Opponent Blockchain not found"); - } - }, - EnumCard::FiftyOnePercentAttack(asset_group_struct) => { - let mut opponent_deck: ComponentDeck = world - .read_model(*asset_group_struct.m_owner); - - // Verify opponent has sets to steal - assert!(opponent_deck.m_sets > 0, "Opponent has no sets"); - - // Get all matching blockchains of the target type - let mut index: usize = 0; - let target_type = asset_group_struct.m_set.at(0).m_bc_type; - let mut blockchains_to_steal = ArrayTrait::new(); - - // First collect all matching blockchains - while index < opponent_deck.m_cards.len() { - let bc = opponent_deck.m_cards.at(index); - match bc { - EnumCard::Blockchain(bc_struct) => { - if bc_struct.m_bc_type == target_type { - blockchains_to_steal.append(bc.clone()); + EnumCard::ReplayAttack(replay_attack_struct) => { + // Retrieve last card played by player from the discard pile. + if let Option::Some(last_card) = discard_pile.m_cards.get(discard_pile.m_cards.len() - 1) { + let unboxed_card = last_card.unbox(); + match unboxed_card { + EnumCard::GasFee(gas_fee_struct) => { + if gas_fee_struct.m_owner == replay_attack_struct.m_owner { + let fee = gas_fee_struct.get_fee() * 2; + match gas_fee_struct.m_players_affected { + EnumPlayerTarget::All(_) => { + let mut index = 0; + let game: ComponentGame = world.read_model(table); + + while index < game.m_players.len() { + let mut player_component: ComponentPlayer = world + .read_model(*game.m_players.at(index)); + player_component.m_in_debt = Option::Some(fee); + world.write_model(@player_component); + index += 1; + }; + }, + EnumPlayerTarget::One(player) => { + let mut player_component: ComponentPlayer = world.read_model(*player); + player_component.m_in_debt = Option::Some(fee); + world.write_model(@player_component); + }, + _ => panic!("Invalid Gas Fee move: No players targeted") + }; + } else { + // Card played too late, potentially punish player... } }, - _ => {} - } - index += 1; - }; - - // Then move all collected blockchains from opponent to self - while let Option::Some(bc) = blockchains_to_steal.pop_front() { - deck.add(bc.clone()); - opponent_deck.remove(@bc.get_name()); - }; - - world.write_model(@deck); - world.write_model(@opponent_deck); + _ => { + // Invalid card played with it, potentially punish player... + } + }; + } + }, + EnumCard::SandwichAttack(_sandwich_attack_struct) => { + let mut game: ComponentGame = world.read_model(table); + game.m_state = EnumGameState::WaitingForRent; + for player in game + .m_players + .span() { + let mut player_component: ComponentPlayer = world.read_model(*player); + player_component.m_in_debt = Option::Some(5); + world.write_model(@player_component); + }; + world.write_model(@game); }, _ => panic!("Invalid or illegal move!") }; + + discard_pile.m_cards.append(card); + world.write_model(@discard_pile); world.write_model(@hand); return (); } - fn _get_owner(ref self: ContractState, card_name: @ByteArray) -> Option { + fn _get_owner(ref self: ContractState, card_name: @ByteArray, table: ContractAddress) -> Option { let mut world = self.world_default(); - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(table); assert!(game.m_state == EnumGameState::Started, "Game has not started yet"); let mut index = 0; diff --git a/src/systems/game.cairo b/src/systems/game.cairo index a3052e5..490fedf 100644 --- a/src/systems/game.cairo +++ b/src/systems/game.cairo @@ -7,10 +7,13 @@ //////////////////////////////// //////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// +use starknet::ContractAddress; + #[starknet::interface] trait IGameSystem { - fn start(ref self: T) -> (); - fn end_turn(ref self: T) -> (); + fn start(ref self: T, table: ContractAddress) -> (); + fn end_turn(ref self: T, table: ContractAddress) -> (); + fn end(ref self: T, table: ContractAddress) -> (); } #[dojo::contract] @@ -20,13 +23,14 @@ mod game_system { ComponentPlayer, ComponentDealer }; use zktt::models::traits::{ - IEnumCard, IPlayer, ICard, IDeck, IDealer, IHand, IGasFee, IAssetGroup, IDraw, IGame, + IEnumCard, IPlayer, ICard, IDeck, IDealer, IHand, IGasFee, IAssetGroup, IGame, IAsset, IBlockchain, IDeposit, IClaimYield, IFiftyOnePercentAttack, IChainReorg, IFrontRun, - ISandwichAttack, IPriorityFee + ISandwichAttack, IPriorityFee, IReplayAttack, IHardFork, ISoftFork, IMEVBoost }; use zktt::models::enums::{ - EnumGasFeeType, EnumPlayerTarget, EnumGameState, EnumBlockchainType, EnumCard + EnumGasFeeType, EnumPlayerTarget, EnumGameState, EnumColor, EnumCard }; + use zktt::systems::player::player_system; use dojo::world::IWorldDispatcher; use core::poseidon::poseidon_hash_span; use starknet::{get_caller_address, get_block_timestamp, get_tx_info, ContractAddress}; @@ -49,13 +53,16 @@ mod game_system { /// Output: /// None. /// Can Panic?: yes - fn start(ref self: ContractState) -> () { + fn start(ref self: ContractState, table: ContractAddress) -> () { let mut world = InternalImpl::world_default(@self); + let mut game: ComponentGame = world.read_model(table); + assert!(game.m_state != EnumGameState::Started, "Game has already started or invalid game ID"); + assert!(game.m_players.len() >= 2, "Missing at least a player before starting"); + assert!(player_system::InternalImpl::_is_everyone_ready(@world, table), "Everyone needs to be ready"); + let cards_in_order = InternalImpl::_create_cards(); let mut flattened_cards = InternalImpl::_flatten(ref world, cards_in_order); - let mut dealer: ComponentDealer = IDealer::new( - world.dispatcher.contract_address, array![] - ); + let mut dealer: ComponentDealer = IDealer::new(table, array![]); let mut index = 0; while let Option::Some(card) = flattened_cards.pop_front() { @@ -66,18 +73,10 @@ mod game_system { }; world.write_model(@dealer); - let seed = world.dispatcher.contract_address; - let mut game: ComponentGame = world.read_model(seed); - - assert!(game.m_state != EnumGameState::Started, "Game has already started"); - assert!(game.m_players.len() >= 2, "Missing at least a player before starting"); - game.m_state = EnumGameState::Started; - let seed: felt252 = InternalImpl::_generate_seed( - @world.dispatcher.contract_address, @game.m_players - ); - let mut dealer: ComponentDealer = world.read_model(world.dispatcher.contract_address); + let seed: felt252 = InternalImpl::_generate_seed(@game.m_players); + let mut dealer: ComponentDealer = world.read_model(table); dealer.shuffle(seed); let mut card_array: Array = ArrayTrait::new(); @@ -88,7 +87,7 @@ mod game_system { card_array.append(card_component.m_card_info); }; - self._distribute_cards(ref game.m_players, ref card_array); + InternalImpl::_distribute_cards(ref world, ref game.m_players, ref card_array); game.assign_next_turn(true); world.write_model(@dealer); @@ -99,20 +98,46 @@ mod game_system { /// next turn. /// /// Inputs: - /// *world*: The mutable reference of the world to write components to. + /// *self*: The mutable reference of the contract to write components to. /// /// Output: /// None. /// Can Panic?: yes - fn end_turn(ref self: ContractState) -> () { + fn end_turn(ref self: ContractState, table: ContractAddress) -> () { let mut world = InternalImpl::world_default(@self); - let mut game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let mut game: ComponentGame = world.read_model(table); assert!(game.m_state == EnumGameState::Started, "Game has not started yet"); assert!(game.m_player_in_turn == get_caller_address(), "Not player's turn"); game.assign_next_turn(false); world.write_model(@game); } + + /// Signal the end of the game. Return all assets and cards to the dealer. Once the game has + /// ended. No one can re-join the table. Called from another contract within the world, not + /// intended for external use. + /// + /// Inputs: + /// *world*: The mutable reference of the world to write components to. + /// + /// Output: + /// None. + /// Can Panic?: yes + fn end(ref self: ContractState, table: ContractAddress) -> () { + // assert!(get_caller_address() == starknet::contract_address_const::<0x0>(), "Unauthorized"); + + let mut world = InternalImpl::world_default(@self); + let mut game: ComponentGame = world.read_model(table); + assert!(game.m_state == EnumGameState::Started, "Game has not started yet"); + + InternalImpl::_assign_winner(ref world); + game.m_state = EnumGameState::Ended; + + // Double check that we have reaquired all assets from all players. + for addr in game.m_players { + player_system::InternalImpl::_relinquish_assets(addr, table, ref world); + }; + } } #[generate_trait] @@ -140,9 +165,7 @@ mod game_system { /// Output: /// The resulting seed hash. /// Can Panic?: yes - fn _generate_seed( - world_address: @ContractAddress, players: @Array - ) -> felt252 { + fn _generate_seed(players: @Array) -> felt252 { let mut array_of_felts: Array = array![ get_block_timestamp().into(), get_tx_info().nonce ]; @@ -169,12 +192,11 @@ mod game_system { /// None. /// Can Panic?: yes fn _distribute_cards( - ref self: ContractState, ref players: Array, ref cards: Array + ref world: dojo::world::WorldStorage, ref players: Array, ref cards: Array ) -> () { if players.is_empty() { panic!("There are no players to distribute cards to!"); } - let mut world = self.world_default(); let mut index = 0; while let Option::Some(player) = players.get(index) { if cards.is_empty() { @@ -196,7 +218,7 @@ mod game_system { /// Create the initial deck of cards for the game in a deterministic manner to then shuffle. /// /// Inputs: - /// *world*: The mutable reference of the world to write components to. + /// None. /// /// Output: /// The deck with one copy of all the card types (unflatten) [59]. @@ -212,68 +234,43 @@ mod game_system { EnumCard::Asset(IAsset::new("ETH [5]", 5, 2)), EnumCard::Asset(IAsset::new("ETH [10]", 10, 1)), // Blockchains. - EnumCard::Blockchain(IBlockchain::new("Aptos", EnumBlockchainType::Grey, 1, 2)), - EnumCard::Blockchain( - IBlockchain::new("Arbitrum", EnumBlockchainType::LightBlue, 1, 2) - ), - EnumCard::Blockchain(IBlockchain::new("Avalanche", EnumBlockchainType::Red, 2, 4)), - EnumCard::Blockchain(IBlockchain::new("Base", EnumBlockchainType::LightBlue, 1, 2)), - EnumCard::Blockchain(IBlockchain::new("Bitcoin", EnumBlockchainType::Gold, 1, 2)), - EnumCard::Blockchain(IBlockchain::new("Blast", EnumBlockchainType::Yellow, 2, 3)), - EnumCard::Blockchain(IBlockchain::new("Canto", EnumBlockchainType::Green, 1, 1)), - EnumCard::Blockchain( - IBlockchain::new("Celestia", EnumBlockchainType::Purple, 2, 3) - ), - EnumCard::Blockchain(IBlockchain::new("Celo", EnumBlockchainType::Yellow, 2, 3)), - EnumCard::Blockchain(IBlockchain::new("Cosmos", EnumBlockchainType::Blue, 1, 1)), - EnumCard::Blockchain(IBlockchain::new("Dogecoin", EnumBlockchainType::Gold, 1, 2)), - EnumCard::Blockchain( - IBlockchain::new("Ethereum", EnumBlockchainType::DarkBlue, 3, 4) - ), - EnumCard::Blockchain( - IBlockchain::new("Fantom", EnumBlockchainType::LightBlue, 1, 2) - ), - EnumCard::Blockchain( - IBlockchain::new("Gnosis Chain", EnumBlockchainType::Green, 1, 1) - ), - EnumCard::Blockchain(IBlockchain::new("Kava", EnumBlockchainType::Red, 2, 4)), - EnumCard::Blockchain(IBlockchain::new("Linea", EnumBlockchainType::Grey, 1, 2)), - EnumCard::Blockchain( - IBlockchain::new("Metis", EnumBlockchainType::LightBlue, 1, 2) - ), - EnumCard::Blockchain(IBlockchain::new("Near", EnumBlockchainType::Green, 1, 1)), - EnumCard::Blockchain(IBlockchain::new("Optimism", EnumBlockchainType::Red, 2, 4)), - EnumCard::Blockchain(IBlockchain::new("Osmosis", EnumBlockchainType::Pink, 1, 1)), - EnumCard::Blockchain(IBlockchain::new("Polkadot", EnumBlockchainType::Pink, 1, 1)), - EnumCard::Blockchain(IBlockchain::new("Polygon", EnumBlockchainType::Purple, 2, 3)), - EnumCard::Blockchain(IBlockchain::new("Scroll", EnumBlockchainType::Yellow, 2, 3)), - EnumCard::Blockchain(IBlockchain::new("Solana", EnumBlockchainType::Purple, 2, 3)), - EnumCard::Blockchain( - IBlockchain::new("Starknet", EnumBlockchainType::DarkBlue, 3, 4) - ), - EnumCard::Blockchain(IBlockchain::new("Taiko", EnumBlockchainType::Pink, 1, 1)), - EnumCard::Blockchain(IBlockchain::new("Ton", EnumBlockchainType::Blue, 1, 1)), - EnumCard::Blockchain(IBlockchain::new("ZKSync", EnumBlockchainType::Grey, 1, 2)), + EnumCard::Blockchain(IBlockchain::new("Aptos", EnumColor::Grey, 1, 2)), + EnumCard::Blockchain(IBlockchain::new("Arbitrum", EnumColor::LightBlue, 1, 2)), + EnumCard::Blockchain(IBlockchain::new("Avalanche", EnumColor::Red, 2, 4)), + EnumCard::Blockchain(IBlockchain::new("Base", EnumColor::LightBlue, 1, 2)), + EnumCard::Blockchain(IBlockchain::new("Bitcoin", EnumColor::Gold, 1, 2)), + EnumCard::Blockchain(IBlockchain::new("Blast", EnumColor::Yellow, 2, 3)), + EnumCard::Blockchain(IBlockchain::new("Canto", EnumColor::Green, 1, 1)), + EnumCard::Blockchain(IBlockchain::new("Celestia", EnumColor::Purple, 2, 3)), + EnumCard::Blockchain(IBlockchain::new("Celo", EnumColor::Yellow, 2, 3)), + EnumCard::Blockchain(IBlockchain::new("Cosmos", EnumColor::Blue, 1, 1)), + EnumCard::Blockchain(IBlockchain::new("Dogecoin", EnumColor::Gold, 1, 2)), + EnumCard::Blockchain(IBlockchain::new("Ethereum", EnumColor::DarkBlue, 3, 4)), + EnumCard::Blockchain(IBlockchain::new("Fantom", EnumColor::LightBlue, 1, 2)), + EnumCard::Blockchain(IBlockchain::new("Gnosis Chain", EnumColor::Green, 1, 1)), + EnumCard::Blockchain(IBlockchain::new("Kava", EnumColor::Red, 2, 4)), + EnumCard::Blockchain(IBlockchain::new("Linea", EnumColor::Grey, 1, 2)), + EnumCard::Blockchain(IBlockchain::new("Metis", EnumColor::LightBlue, 1, 2)), + EnumCard::Blockchain(IBlockchain::new("Near", EnumColor::Green, 1, 1)), + EnumCard::Blockchain(IBlockchain::new("Optimism", EnumColor::Red, 2, 4)), + EnumCard::Blockchain(IBlockchain::new("Osmosis", EnumColor::Pink, 1, 1)), + EnumCard::Blockchain(IBlockchain::new("Polkadot", EnumColor::Pink, 1, 1)), + EnumCard::Blockchain(IBlockchain::new("Polygon", EnumColor::Purple, 2, 3)), + EnumCard::Blockchain(IBlockchain::new("Scroll", EnumColor::Yellow, 2, 3)), + EnumCard::Blockchain(IBlockchain::new("Solana", EnumColor::Purple, 2, 3)), + EnumCard::Blockchain(IBlockchain::new("Starknet", EnumColor::DarkBlue, 3, 4)), + EnumCard::Blockchain(IBlockchain::new("Taiko", EnumColor::Pink, 1, 1)), + EnumCard::Blockchain(IBlockchain::new("Ton", EnumColor::Blue, 1, 1)), + EnumCard::Blockchain(IBlockchain::new("ZKSync", EnumColor::Grey, 1, 2)), // Actions. - EnumCard::PriorityFee(IPriorityFee::new(1, 10)), - EnumCard::ClaimYield(IClaimYield::new(2, 3)), - EnumCard::FiftyOnePercentAttack( - IFiftyOnePercentAttack::new( - starknet::contract_address_const::<0x0>(), array![], 5, 1 - ) - ), - EnumCard::ChainReorg( - IChainReorg::new("", "", starknet::contract_address_const::<0x0>(), 3, 3) - ), - EnumCard::FrontRun(IFrontRun::new("", 3, 3)), - EnumCard::SandwichAttack(ISandwichAttack::new(3, 3)), - // EnumCard::ReplayAttack(IReplayAttack::new(1, 2)), + EnumCard::ChainReorg(IChainReorg::default()), + EnumCard::ClaimYield(IClaimYield::default()), + EnumCard::FiftyOnePercentAttack(IFiftyOnePercentAttack::default()), + EnumCard::FrontRun(IFrontRun::default()), EnumCard::GasFee( IGasFee::new( EnumPlayerTarget::All, - EnumGasFeeType::AgainstTwo( - (EnumBlockchainType::DarkBlue, EnumBlockchainType::Red) - ), + EnumGasFeeType::AgainstTwo((EnumColor::DarkBlue, EnumColor::Red)), array![], 1, 2 @@ -282,9 +279,7 @@ mod game_system { EnumCard::GasFee( IGasFee::new( EnumPlayerTarget::All, - EnumGasFeeType::AgainstTwo( - (EnumBlockchainType::Yellow, EnumBlockchainType::Purple) - ), + EnumGasFeeType::AgainstTwo((EnumColor::Yellow, EnumColor::Purple)), array![], 1, 2 @@ -293,9 +288,7 @@ mod game_system { EnumCard::GasFee( IGasFee::new( EnumPlayerTarget::All, - EnumGasFeeType::AgainstTwo( - (EnumBlockchainType::Green, EnumBlockchainType::LightBlue) - ), + EnumGasFeeType::AgainstTwo((EnumColor::Green, EnumColor::LightBlue)), array![], 1, 2 @@ -304,9 +297,7 @@ mod game_system { EnumCard::GasFee( IGasFee::new( EnumPlayerTarget::All, - EnumGasFeeType::AgainstTwo( - (EnumBlockchainType::Grey, EnumBlockchainType::Pink) - ), + EnumGasFeeType::AgainstTwo((EnumColor::Grey, EnumColor::Pink)), array![], 1, 2 @@ -315,17 +306,24 @@ mod game_system { EnumCard::GasFee( IGasFee::new( EnumPlayerTarget::All, - EnumGasFeeType::AgainstTwo( - (EnumBlockchainType::Blue, EnumBlockchainType::Gold) - ), + EnumGasFeeType::AgainstTwo((EnumColor::Blue, EnumColor::Gold)), array![], 1, 2 ) ), EnumCard::GasFee( - IGasFee::new(EnumPlayerTarget::None, EnumGasFeeType::Any(()), array![], 3, 3) + IGasFee::new( + EnumPlayerTarget::None, + EnumGasFeeType::Any(()), + array![], 3, 3) ), + EnumCard::HardFork(IHardFork::default()), + EnumCard::MEVBoost(IMEVBoost::default()), + EnumCard::PriorityFee(IPriorityFee::default()), + EnumCard::ReplayAttack(IReplayAttack::default()), + EnumCard::SandwichAttack(ISandwichAttack::default()), + EnumCard::SoftFork(ISoftFork::default()), ]; return cards_in_order; @@ -335,6 +333,7 @@ mod game_system { /// dealer. /// /// Inputs: + /// *world*: The mutable reference of the world to write components to. /// *container*: The deck with one copy of all the card types (unflatten) [59]. /// /// Output: @@ -354,5 +353,18 @@ mod game_system { }; return flattened_array; } + + /// Assign the winner when the game ends. Upon classic end, the winner is attributed to the + /// one who gets [3] complete sets in their deck. There can only be one winner. + /// + /// Inputs: + /// *world*: The mutable reference of the world to write components to. + /// + /// Output: + /// None. + /// Can Panic?: yes + fn _assign_winner(ref world: dojo::world::WorldStorage) -> () { + // TODO: Determine the winner upon interrupt and reward for the winner + } } } diff --git a/src/systems/player.cairo b/src/systems/player.cairo index be96033..8101dc6 100644 --- a/src/systems/player.cairo +++ b/src/systems/player.cairo @@ -7,24 +7,29 @@ //////////////////////////////// //////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// +use starknet::ContractAddress; + #[starknet::interface] trait IPlayerSystem { - fn join(ref self: T, username: ByteArray) -> (); - fn leave(ref self: T) -> (); + fn join(ref self: T, username: ByteArray, table: ContractAddress) -> (); + fn set_ready(ref self: T, ready: bool, table: ContractAddress) -> (); + fn leave(ref self: T, table: ContractAddress) -> (); } #[dojo::contract] mod player_system { use zktt::models::components::{ ComponentGame, ComponentHand, ComponentDeck, ComponentDeposit, ComponentPlayer, - ComponentDealer + ComponentDealer, ComponentDiscardPile }; use zktt::models::traits::{ - IEnumCard, IPlayer, IDeck, IDealer, IHand, IGasFee, IAssetGroup, IDraw, IGame, IAsset, + IEnumCard, IPlayer, IDeck, IDealer, IHand, IGasFee, IAssetGroup, IGame, IAsset, IBlockchain, IDeposit }; use zktt::models::enums::{EnumGasFeeType, EnumPlayerTarget, EnumGameState}; use dojo::world::IWorldDispatcher; + use starknet::ContractAddress; + use zktt::systems::game::{IGameSystemDispatcher, IGameSystemDispatcherTrait}; use starknet::get_caller_address; use dojo::model::ModelStorage; @@ -40,7 +45,7 @@ mod player_system { /// yet. /// /// Inputs: - /// *world*: The mutable reference of the world to write components to. + /// *self*: The mutable reference of the contract to write components to. /// *username*: The user-selected displayed name identifyinh the current player's name. /// Note that the current implementation allows for multiple users to have the same /// username. @@ -48,11 +53,11 @@ mod player_system { /// Output: /// None. /// Can Panic?: yes - fn join(ref self: ContractState, username: ByteArray) -> () { + fn join(ref self: ContractState, username: ByteArray, table: ContractAddress) -> () { let mut world = InternalImpl::world_default(@self); - let mut game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let mut game: ComponentGame = world.read_model(table); assert!(game.m_state != EnumGameState::Started, "Game has already started"); - assert!(game.m_players.len() < 5, "Lobby already full"); + assert!(game.m_players.len() < 5, "Lobby full"); let caller = get_caller_address(); let player = IPlayer::new(caller, username); @@ -61,33 +66,63 @@ mod player_system { world.write_model(@player); } - /// Make the caller's player leave the table and surrender all cards to the discard pile. + /// Allows a player to set their ready status for the upcoming game. Once every player is + /// ready, we start the game. Cannot be called once the game has already started. /// /// Inputs: - /// *world*: The mutable reference of the world to write components to. + /// *self*: The mutable reference of the contract to write components to. + /// *ready*: Toggle readyness. + /// + /// Output: + /// None. + /// Can Panic?: yes + fn set_ready(ref self: ContractState, ready: bool, table: ContractAddress) -> () { + let mut world = InternalImpl::world_default(@self); + let mut game: ComponentGame = world.read_model(table); + assert!(game.m_state != EnumGameState::Started, "Game has already started"); + + let mut player: ComponentPlayer = world.read_model(get_caller_address()); + player.m_is_ready = ready; + world.write_model(@player); + + // Start the game if everyone is ready. + if game.m_players.len() >= 2 && InternalImpl::_is_everyone_ready(@world, table) { + let mut game_system: IGameSystemDispatcher = IGameSystemDispatcher { + contract_address: table + }; + game_system.start(table); + } + } + + /// Make current caller leave the ongoing pre-game lobby OR ongoing game. The player gives + /// back all the cards they own to the table in the discard pile upon exiting. + /// + /// Once a player leaves they CANNOT come back to the table IF the game has started. + /// + /// Inputs: + /// *self*: The mutable reference of the contract to write components to. /// /// Output: /// None. /// Can Panic?: yes - fn leave(ref self: ContractState) -> () { + fn leave(ref self: ContractState, table: ContractAddress) -> () { let mut world = InternalImpl::world_default(@self); - let mut game: ComponentGame = world.read_model(world.dispatcher.contract_address); - assert!(game.m_state == EnumGameState::Started, "Game has not started yet"); + let mut game: ComponentGame = world.read_model(table); assert!(game.contains_player(@get_caller_address()).is_some(), "Player not found"); - let mut hand: ComponentHand = world.read_model(get_caller_address()); - let mut deck: ComponentDeck = world.read_model(get_caller_address()); - let mut deposit: ComponentDeposit = world.read_model(get_caller_address()); + let caller = get_caller_address(); + // Give all cards back to the board. + InternalImpl::_relinquish_assets(caller, table, ref world); + game.remove_player(@caller); - // Cleanup after player by setting all card owner's to 0. - game.remove_player(@get_caller_address()); - if game.m_players.is_empty() { - game.m_state = EnumGameState::Ended; + // Check if there's at least two players left, otherwise end the ongoing game. + if game.m_players.len() < 2 && game.m_state == EnumGameState::Started { + let mut game_system: IGameSystemDispatcher = IGameSystemDispatcher { + contract_address: table + }; + game_system.end(table); } - world.erase_model(@hand); - world.erase_model(@deck); - world.erase_model(@deposit); world.write_model(@game); return (); } @@ -106,5 +141,65 @@ mod player_system { fn world_default(self: @ContractState) -> dojo::world::WorldStorage { self.world(@"zktt") } + + /// Check if every player at the table is ready to start the game. Game will only start if + /// ALL players are ready (Might impl a timer of some sort to prevent griefing in the front + /// end. + /// + /// Inputs: + /// *world*: The immutable reference of the world to read components from. + /// + /// Output: + /// None. + /// Can Panic?: yes + fn _is_everyone_ready(world: @dojo::world::WorldStorage, table: ContractAddress) -> bool { + let game: ComponentGame = world.read_model(table); + let mut everyone_ready: bool = true; + + for addr in game.m_players { + let player: ComponentPlayer = world.read_model(addr); + if !player.m_is_ready { + everyone_ready = false; + } + }; + return everyone_ready; + } + + /// Take all cards owned by player and put them in the discard pile, effectively re-possessing + /// all player cards back. Normally after player leaves or game ends. + /// + /// Inputs: + /// *player_address*: The contract address of the player in question. + /// *world*: The mutable reference of the world to write components to. + /// + /// Output: + /// None. + /// Can Panic?: yes + fn _relinquish_assets(player_address: ContractAddress, table: ContractAddress, ref world: dojo::world::WorldStorage) -> () { + let mut hand: ComponentHand = world.read_model(player_address); + let mut deck: ComponentDeck = world.read_model(player_address); + let mut deposit: ComponentDeposit = world.read_model(player_address); + let mut discard_pile: ComponentDiscardPile = world.read_model(table); + + // Put everything owned into discard pile. + for card in hand.m_cards.span() { + discard_pile.m_cards.append(card.clone()); + }; + + for card in deck.m_cards.span() { + discard_pile.m_cards.append(card.clone()); + }; + + for card in deposit.m_cards.span() { + discard_pile.m_cards.append(card.clone()); + }; + + // Delete all model references of player. + world.erase_model(@hand); + world.erase_model(@deck); + world.erase_model(@deposit); + + world.write_model(@discard_pile); + } } } From 959018d3f3adc8da5b901925b5a99b4e5dcef1e7 Mon Sep 17 00:00:00 2001 From: Nami Reghbati Date: Wed, 27 Nov 2024 15:49:49 -0500 Subject: [PATCH 03/15] add: Added Jetbrains ide config ignore. --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 701615d..17d3b3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# IDE +.idea/* + # dependencies /node_modules /target @@ -43,4 +46,4 @@ report.*.json # .vscode Debugging .vscode/*-debug-profile -.vscode \ No newline at end of file +.vscode From 8cc41398a629fc437ba8f8cb4b3f88c2480e6773 Mon Sep 17 00:00:00 2001 From: Nami Reghbati Date: Wed, 27 Nov 2024 15:50:26 -0500 Subject: [PATCH 04/15] chore: Updated tests and made them work with new structure. --- src/tests/integration/test_actions.cairo | 119 ++++++++-------- src/tests/integration/test_cards.cairo | 169 ++++++++++++----------- src/tests/integration/test_game.cairo | 132 +++++++++--------- src/tests/integration/test_player.cairo | 91 ++++++------ src/tests/utils.cairo | 3 +- 5 files changed, 268 insertions(+), 246 deletions(-) diff --git a/src/tests/integration/test_actions.cairo b/src/tests/integration/test_actions.cairo index 8f9554b..79e4779 100644 --- a/src/tests/integration/test_actions.cairo +++ b/src/tests/integration/test_actions.cairo @@ -24,13 +24,13 @@ use starknet::ContractAddress; use crate::models::enums::{EnumCard, EnumGameState}; use crate::systems::actions::action_system; use crate::systems::actions::{IActionSystemDispatcher, IActionSystemDispatcherTrait}; -use crate::systems::game::{IGameSystemDispatcher, IGameSystemDispatcherTrait, game_system}; +use crate::systems::game::{game_system, IGameSystemDispatcher, IGameSystemDispatcherTrait}; use crate::systems::player::{IPlayerSystemDispatcher, IPlayerSystemDispatcherTrait}; use crate::models::components::{ ComponentGame, ComponentHand, ComponentDeposit, ComponentPlayer, ComponentDeck, ComponentDealer }; use crate::models::traits::{ComponentPlayerDisplay, IDealer, IAsset, IClaimYield, IHand}; -use crate::tests::utils::namespace_def; +use crate::tests::utils::{deploy_world, namespace_def}; use crate::tests::integration::test_game::deploy_game; use crate::tests::integration::test_player::deploy_player; @@ -58,31 +58,31 @@ pub fn deploy_actions(ref world: WorldStorage) -> IActionSystemDispatcher { fn test_draw() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system: IActionSystemDispatcher = deploy_actions(ref world); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); - // Set player one as the next caller - starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); - starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - // Provide deterministic seed starknet::testing::set_block_timestamp(240); starknet::testing::set_nonce(0x111); - game_system.start(); + // Set player one as the next caller + starknet::testing::set_contract_address(first_caller); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); + starknet::testing::set_contract_address(second_caller); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); - let mut dealer: ComponentDealer = world.read_model(world.dispatcher.contract_address); + let mut dealer: ComponentDealer = world.read_model(addr); assert!(!dealer.m_cards.is_empty(), "Dealer should have cards!"); // Set player one as the next caller starknet::testing::set_contract_address(first_caller); // Draw cards - action_system.draw(false); + action_system.draw(false, addr); // Verify hand size increased let hand: ComponentHand = world.read_model(first_caller); @@ -98,28 +98,28 @@ fn test_draw() { fn test_draw_twice_in_turn() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system: IActionSystemDispatcher = deploy_actions(ref world); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); - // Setup game with two players - starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); - starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - // Provide deterministic seed starknet::testing::set_block_timestamp(240); starknet::testing::set_nonce(0x111); - game_system.start(); + // Setup game with two players + starknet::testing::set_contract_address(first_caller); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); + starknet::testing::set_contract_address(second_caller); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Set player one as the next caller starknet::testing::set_contract_address(first_caller); // First draw - should succeed - action_system.draw(false); + action_system.draw(false, addr); // Verify first draw succeeded let hand: ComponentHand = world.read_model(first_caller); @@ -129,37 +129,38 @@ fn test_draw_twice_in_turn() { assert!(player.m_has_drawn, "Player should be marked as having drawn"); // Second draw - should panic - action_system.draw(false); + action_system.draw(false, addr); } #[test] fn test_play() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system: IActionSystemDispatcher = deploy_actions(ref world); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Setup game state starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Set player one as the next caller starknet::testing::set_contract_address(first_caller); // Draw cards first - action_system.draw(false); + action_system.draw(false, addr); // Get a card to play let hand: ComponentHand = world.read_model(first_caller); let card: EnumCard = hand.m_cards.at(0).clone(); // Play the card - action_system.play(card); + action_system.play(card, addr); // Verify player state let player: ComponentPlayer = world.read_model(first_caller); @@ -174,30 +175,31 @@ fn test_play() { fn test_move() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system: IActionSystemDispatcher = deploy_actions(ref world); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Setup game state starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Set player one as the next caller starknet::testing::set_contract_address(first_caller); // Draw cards first - action_system.draw(false); + action_system.draw(false, addr); // Get a card to move let hand: ComponentHand = world.read_model(first_caller); let card: EnumCard = hand.m_cards.at(0).clone(); // Move the card - action_system.move(card); + action_system.move(card, addr); // Verify player state unchanged since move doesn't consume moves let player: ComponentPlayer = world.read_model(first_caller); @@ -208,21 +210,22 @@ fn test_move() { fn test_claim_yield_and_pay_fee() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let mut world: WorldStorage = deploy_world(); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let action_system: IActionSystemDispatcher = deploy_actions(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Set player one as the next caller and draw cards starknet::testing::set_contract_address(first_caller); - action_system.draw(false); + action_system.draw(false, addr); // Get ClaimYield card and play it let mut hand: ComponentHand = world.read_model(first_caller); @@ -231,10 +234,10 @@ fn test_claim_yield_and_pay_fee() { world.write_model_test(@hand); // Play ClaimYield card - action_system.play(claim_yield_card); + action_system.play(claim_yield_card, addr); // Verify game is paused and players are in debt - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(addr); assert!(game.m_state == EnumGameState::WaitingForRent, "Game should be paused"); let player2: ComponentPlayer = world.read_model(second_caller); @@ -249,10 +252,10 @@ fn test_claim_yield_and_pay_fee() { world.write_model_test(@player2_deposit); // Pay the debt - action_system.pay_fee(payment_cards, first_caller, second_caller); + action_system.pay_fee(payment_cards, first_caller, second_caller, addr); // Verify game resumed and debt cleared - let game_after: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game_after: ComponentGame = world.read_model(addr); assert!(game_after.m_state == EnumGameState::Started, "Game should resume after payment"); let player2_after: ComponentPlayer = world.read_model(second_caller); @@ -271,7 +274,7 @@ fn test_claim_yield_and_pay_fee() { let new_asset = EnumCard::Asset(IAsset::new("ETH [2]", 2, 1)); hand2.add(new_asset.clone()); world.write_model_test(@hand2); - action_system.play(new_asset); // Should not panic + action_system.play(new_asset, addr); // Should not panic } #[test] @@ -279,26 +282,28 @@ fn test_claim_yield_and_pay_fee() { fn test_actions_blocked_during_debt() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let mut world: WorldStorage = deploy_world(); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let action_system: IActionSystemDispatcher = deploy_actions(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Player 1 plays ClaimYield starknet::testing::set_contract_address(first_caller); - action_system.draw(false); + action_system.draw(false, addr); + let mut hand: ComponentHand = world.read_model(first_caller); let claim_yield_card = EnumCard::ClaimYield(IClaimYield::new(2, 3)); hand.add(claim_yield_card.clone()); world.write_model_test(@hand); - action_system.play(claim_yield_card); + action_system.play(claim_yield_card, addr); // Player 2 tries to play a card while in debt - should fail starknet::testing::set_contract_address(second_caller); @@ -306,5 +311,5 @@ fn test_actions_blocked_during_debt() { let mut hand2: ComponentHand = world.read_model(second_caller); hand2.add(test_asset.clone()); world.write_model_test(@hand2); - action_system.play(test_asset); // This should panic + action_system.play(test_asset, addr); // This should panic } diff --git a/src/tests/integration/test_cards.cairo b/src/tests/integration/test_cards.cairo index ac8377c..f91fef2 100644 --- a/src/tests/integration/test_cards.cairo +++ b/src/tests/integration/test_cards.cairo @@ -22,7 +22,7 @@ use starknet::ContractAddress; use crate::models::structs::StructBlockchain; use crate::models::enums::{ - EnumCard, EnumGameState, EnumBlockchainType, EnumGasFeeType, EnumPlayerTarget + EnumCard, EnumGameState, EnumColor, EnumGasFeeType, EnumPlayerTarget }; use crate::models::components::{ ComponentGame, ComponentHand, ComponentDeposit, ComponentPlayer, ComponentDeck, ComponentDealer @@ -31,12 +31,12 @@ use crate::models::traits::{ IAsset, IBlockchain, IClaimYield, ISandwichAttack, IGasFee, IPriorityFee, IFrontRun, IFiftyOnePercentAttack, IChainReorg, IHand, IDeck, ComponentDeckDisplay }; -use crate::tests::utils::namespace_def; +use crate::tests::utils::{deploy_world, namespace_def}; use crate::tests::integration::test_game::deploy_game; use crate::tests::integration::test_player::deploy_player; use crate::tests::integration::test_actions::deploy_actions; -use crate::systems::game::{IGameSystemDispatcher, IGameSystemDispatcherTrait}; +use crate::systems::game::{game_system, IGameSystemDispatcher, IGameSystemDispatcherTrait}; use crate::systems::player::{IPlayerSystemDispatcher, IPlayerSystemDispatcherTrait}; use crate::systems::actions::{IActionSystemDispatcher, IActionSystemDispatcherTrait}; @@ -48,23 +48,24 @@ use dojo_cairo_test::{spawn_test_world, WorldStorageTestTrait}; fn test_asset_card() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system = deploy_actions(ref world); - let _game_system = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - _game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Set player one as the next caller starknet::testing::set_contract_address(first_caller); // Draw cards first - action_system.draw(false); + action_system.draw(false, addr); // Create and play asset card let mut hand: ComponentHand = world.read_model(first_caller); @@ -73,7 +74,7 @@ fn test_asset_card() { world.write_model_test(@hand); // Play asset card - action_system.play(asset_card); + action_system.play(asset_card, addr); // Verify asset added to deposit let deposit: ComponentDeposit = world.read_model(first_caller); @@ -84,32 +85,33 @@ fn test_asset_card() { fn test_blockchain_card() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system = deploy_actions(ref world); - let _game_system = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - _game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Set player one as the next caller and draw cards starknet::testing::set_contract_address(first_caller); - action_system.draw(false); + action_system.draw(false, addr); // Create and play blockchain card let mut hand: ComponentHand = world.read_model(first_caller); let blockchain_card = EnumCard::Blockchain( - IBlockchain::new("Ethereum", EnumBlockchainType::DarkBlue, 3, 4) + IBlockchain::new("Ethereum", EnumColor::DarkBlue, 3, 4) ); hand.add(blockchain_card.clone()); world.write_model_test(@hand); // Play blockchain card - action_system.play(blockchain_card); + action_system.play(blockchain_card, addr); // Verify blockchain added to deck let deck: ComponentDeck = world.read_model(first_caller); @@ -120,31 +122,32 @@ fn test_blockchain_card() { fn test_claim_yield_card() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system = deploy_actions(ref world); - let _game_system = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - _game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Set player one as the next caller and draw cards first starknet::testing::set_contract_address(first_caller); - action_system.draw(false); + action_system.draw(false, addr); // Play ClaimYield card let mut hand: ComponentHand = world.read_model(first_caller); let claim_yield_card = EnumCard::ClaimYield(IClaimYield::new(2, 3)); hand.add(claim_yield_card.clone()); world.write_model_test(@hand); - action_system.play(claim_yield_card); + action_system.play(claim_yield_card, addr); // Verify effects - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(addr); assert!(game.m_state == EnumGameState::WaitingForRent, "Game should be waiting for rent"); let player2: ComponentPlayer = world.read_model(second_caller); @@ -155,31 +158,32 @@ fn test_claim_yield_card() { fn test_sandwich_attack_card() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system = deploy_actions(ref world); - let _game_system = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - _game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Set player one as the next caller and draw cards starknet::testing::set_contract_address(first_caller); - action_system.draw(false); + action_system.draw(false, addr); // Play SandwichAttack card let mut hand: ComponentHand = world.read_model(first_caller); - let sandwich_attack_card = EnumCard::SandwichAttack(ISandwichAttack::new(3, 3)); + let sandwich_attack_card = EnumCard::SandwichAttack(ISandwichAttack::new(second_caller, 3, 3)); hand.add(sandwich_attack_card.clone()); world.write_model_test(@hand); - action_system.play(sandwich_attack_card); + action_system.play(sandwich_attack_card, addr); // Verify effects - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(addr); assert!(game.m_state == EnumGameState::WaitingForRent, "Game should be waiting for rent"); let player2: ComponentPlayer = world.read_model(second_caller); @@ -190,21 +194,22 @@ fn test_sandwich_attack_card() { fn test_gas_fee_card() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system = deploy_actions(ref world); - let _game_system = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - _game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Set player one as the next caller and draw cards starknet::testing::set_contract_address(first_caller); - action_system.draw(false); + action_system.draw(false, addr); // Create GasFee card targeting specific color let mut hand: ComponentHand = world.read_model(first_caller); @@ -215,7 +220,7 @@ fn test_gas_fee_card() { array![ StructBlockchain { m_name: "Ethereum", - m_bc_type: EnumBlockchainType::DarkBlue, + m_bc_type: EnumColor::DarkBlue, m_fee: 3, m_value: 4 } @@ -229,10 +234,10 @@ fn test_gas_fee_card() { world.write_model_test(@hand); // Play GasFee card - action_system.play(gas_fee_card); + action_system.play(gas_fee_card, addr); // Verify effects - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(addr); assert!(game.m_state == EnumGameState::WaitingForRent, "Game should be waiting for rent"); } @@ -240,21 +245,22 @@ fn test_gas_fee_card() { fn test_priority_fee_card() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system = deploy_actions(ref world); - let _game_system = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - _game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Set player one as the next caller and draw cards starknet::testing::set_contract_address(first_caller); - action_system.draw(false); + action_system.draw(false, addr); // Play PriorityFee card let mut hand: ComponentHand = world.read_model(first_caller); @@ -263,7 +269,7 @@ fn test_priority_fee_card() { world.write_model_test(@hand); let initial_hand_size = hand.m_cards.len(); - action_system.play(priority_fee_card); + action_system.play(priority_fee_card, addr); // Verify player drew 2 additional cards let hand_after: ComponentHand = world.read_model(first_caller); @@ -277,21 +283,22 @@ fn test_priority_fee_card() { fn test_frontrun_card() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system = deploy_actions(ref world); - let _game_system = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - _game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Setup target blockchain in player 2's deck let target_blockchain = EnumCard::Blockchain( - IBlockchain::new("Ethereum", EnumBlockchainType::DarkBlue, 3, 4) + IBlockchain::new("Ethereum", EnumColor::DarkBlue, 3, 4) ); let mut player2_deck: ComponentDeck = world.read_model(second_caller); player2_deck.add(target_blockchain.clone()); @@ -299,14 +306,14 @@ fn test_frontrun_card() { // Set player one as the next caller and draw cards first starknet::testing::set_contract_address(first_caller); - action_system.draw(false); + action_system.draw(false, addr); // Play FrontRun card from player 1 let mut hand: ComponentHand = world.read_model(first_caller); - let frontrun_card = EnumCard::FrontRun(IFrontRun::new("Ethereum", 3, 3)); + let frontrun_card = EnumCard::FrontRun(IFrontRun::new(second_caller, "Ethereum", 3, 3)); hand.add(frontrun_card.clone()); world.write_model_test(@hand); - action_system.play(frontrun_card); + action_system.play(frontrun_card, addr); // Verify blockchain was stolen let player1_deck: ComponentDeck = world.read_model(first_caller); @@ -319,28 +326,29 @@ fn test_frontrun_card() { fn test_chain_reorg_card() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system = deploy_actions(ref world); - let _game_system = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - _game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Set player one as the next caller and draw cards starknet::testing::set_contract_address(first_caller); - action_system.draw(false); + action_system.draw(false, addr); // Setup blockchains to swap let blockchain1 = EnumCard::Blockchain( - IBlockchain::new("Ethereum", EnumBlockchainType::DarkBlue, 3, 4) + IBlockchain::new("Ethereum", EnumColor::DarkBlue, 3, 4) ); let blockchain2 = EnumCard::Blockchain( - IBlockchain::new("Bitcoin", EnumBlockchainType::Gold, 1, 2) + IBlockchain::new("Bitcoin", EnumColor::Gold, 1, 2) ); // Add blockchains to respective decks @@ -359,7 +367,7 @@ fn test_chain_reorg_card() { hand.add(chain_reorg_card.clone()); world.write_model_test(@hand); - action_system.play(chain_reorg_card); + action_system.play(chain_reorg_card, addr); // Verify blockchains were swapped let player1_deck_after: ComponentDeck = world.read_model(first_caller); @@ -372,38 +380,39 @@ fn test_chain_reorg_card() { fn test_fifty_one_percent_attack_card() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let action_system = deploy_actions(ref world); - let _game_system = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - _game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Setup target asset group in player 2's deck let blockchain_set: Array = array![ - IBlockchain::new("Ethereum", EnumBlockchainType::DarkBlue, 3, 4), - IBlockchain::new("Starknet", EnumBlockchainType::DarkBlue, 3, 4) + IBlockchain::new("Ethereum", EnumColor::DarkBlue, 3, 4), + IBlockchain::new("Starknet", EnumColor::DarkBlue, 3, 4) ]; let mut player2_deck: ComponentDeck = world.read_model(second_caller); player2_deck .add( - EnumCard::Blockchain(IBlockchain::new("Ethereum", EnumBlockchainType::DarkBlue, 3, 4)) + EnumCard::Blockchain(IBlockchain::new("Ethereum", EnumColor::DarkBlue, 3, 4)) ); player2_deck .add( - EnumCard::Blockchain(IBlockchain::new("Starknet", EnumBlockchainType::DarkBlue, 3, 4)) + EnumCard::Blockchain(IBlockchain::new("Starknet", EnumColor::DarkBlue, 3, 4)) ); world.write_model_test(@player2_deck); // Set player one as the next caller and draw cards first starknet::testing::set_contract_address(first_caller); - action_system.draw(false); + action_system.draw(false, addr); // Play FiftyOnePercentAttack card let mut hand: ComponentHand = world.read_model(first_caller); @@ -412,7 +421,7 @@ fn test_fifty_one_percent_attack_card() { ); hand.add(fifty_one_percent_card.clone()); world.write_model_test(@hand); - action_system.play(fifty_one_percent_card); + action_system.play(fifty_one_percent_card, addr); // Verify asset group was stolen let player1_deck: ComponentDeck = world.read_model(first_caller); diff --git a/src/tests/integration/test_game.cairo b/src/tests/integration/test_game.cairo index 5f47ca1..01b3f8a 100644 --- a/src/tests/integration/test_game.cairo +++ b/src/tests/integration/test_game.cairo @@ -28,7 +28,7 @@ use dojo_cairo_test::WorldStorageTestTrait; use dojo::model::Model; use dojo_cairo_test::{spawn_test_world, NamespaceDef, TestResource, ContractDefTrait}; -use crate::tests::utils::namespace_def; +use crate::tests::utils::{deploy_world, namespace_def}; use crate::tests::integration::test_player::deploy_player; use crate::tests::integration::test_actions::deploy_actions; @@ -42,7 +42,7 @@ use crate::models::enums::{EnumGameState}; use crate::models::traits::{ComponentPlayerDisplay, IDealer}; // Deploy world with supplied components registered. -pub fn deploy_game(ref world: WorldStorage) -> IGameSystemDispatcher { +pub fn deploy_game(ref world: WorldStorage) -> (ContractAddress, IGameSystemDispatcher) { let (contract_address, _) = world.dns(@"game_system").unwrap(); let system: IGameSystemDispatcher = IGameSystemDispatcher { contract_address }; @@ -62,64 +62,59 @@ pub fn deploy_game(ref world: WorldStorage) -> IGameSystemDispatcher { }; world.write_model(@dealer); - return system; + return (contract_address, system); } #[test] fn test_start() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let mut world: WorldStorage = deploy_world(); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); let mut dealer: ComponentDealer = world.read_model(world.dispatcher.contract_address); assert!(!dealer.m_cards.is_empty(), "Dealer should have cards!"); - // Set player one as the next caller. - starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); - - // Set player two as the next caller. - starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - // Provide deterministic seed starknet::testing::set_block_timestamp(240); starknet::testing::set_nonce(0x111); - // Start the game. - game_system.start(); + // Set player one as the next caller. + starknet::testing::set_contract_address(first_caller); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); + starknet::testing::set_contract_address(second_caller); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); + + let game: ComponentGame = world.read_model(addr); + assert!(game.m_state == EnumGameState::Started, "Game should have started!"); // Check players' hands. let player1_hand: ComponentHand = world.read_model(first_caller); assert!(player1_hand.m_cards.len() == 5, "Player 1 should have received 5 cards!"); let player2_hand: ComponentHand = world.read_model(second_caller); assert!(player2_hand.m_cards.len() == 5, "Player 2 should have received 5 cards!"); - - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); - assert!(game.m_state == EnumGameState::Started, "Game should have started!"); } #[test] fn test_new_turn() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let mut world: WorldStorage = deploy_world(); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Set player one as the next caller. starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); - - // Set player two as the next caller. + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); - game_system.start(); - - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(addr); assert!(game.m_player_in_turn == first_caller, "Player 1 should have started their turn!"); } @@ -127,28 +122,28 @@ fn test_new_turn() { fn test_end_turn() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let mut world: WorldStorage = deploy_world(); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - - game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Verify initial turn - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(addr); assert!(game.m_player_in_turn == first_caller, "Player 1 should start"); // End turn as first player starknet::testing::set_contract_address(first_caller); - game_system.end_turn(); + _game_system.end_turn(addr); // Verify turn passed to second player - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(addr); assert!(game.m_player_in_turn == second_caller, "Turn should pass to Player 2"); } @@ -156,10 +151,10 @@ fn test_end_turn() { #[should_panic(expected: ("Game has not started yet", 'ENTRYPOINT_FAILED'))] fn test_end_turn_before_game_starts() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let mut world: WorldStorage = deploy_world(); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); starknet::testing::set_contract_address(first_caller); - game_system.end_turn(); + _game_system.end_turn(addr); } #[test] @@ -167,82 +162,85 @@ fn test_end_turn_before_game_starts() { fn test_end_turn_wrong_player() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let mut world: WorldStorage = deploy_world(); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - - game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Try to end turn as second player when it's first player's turn starknet::testing::set_contract_address(second_caller); - game_system.end_turn(); + _game_system.end_turn(addr); } #[test] #[should_panic(expected: ("Missing at least a player before starting", 'ENTRYPOINT_FAILED'))] fn test_start_with_one_player() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let mut world: WorldStorage = deploy_world(); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Try to start with just one player starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); - game_system.start(); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); + + starknet::testing::set_contract_address(starknet::contract_address_const::<0x0>()); + _game_system.start(addr); } #[test] -#[should_panic(expected: ("Game has already started", 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ("Game has already started or invalid game ID", 'ENTRYPOINT_FAILED'))] fn test_start_game_twice() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let mut world: WorldStorage = deploy_world(); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Setup and start game normally starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - - game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Try to start again - game_system.start(); + _game_system.start(addr); } #[test] fn test_full_turn_cycle() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let mut world: WorldStorage = deploy_world(); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - - game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Complete a full turn cycle starknet::testing::set_contract_address(first_caller); - game_system.end_turn(); + _game_system.end_turn(addr); starknet::testing::set_contract_address(second_caller); - game_system.end_turn(); + _game_system.end_turn(addr); // Verify turn returned to first player - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(addr); assert!(game.m_player_in_turn == first_caller, "Turn should cycle back to Player 1"); } diff --git a/src/tests/integration/test_player.cairo b/src/tests/integration/test_player.cairo index 21e889c..bf6a7af 100644 --- a/src/tests/integration/test_player.cairo +++ b/src/tests/integration/test_player.cairo @@ -26,7 +26,7 @@ use crate::models::components::{ ComponentGame, ComponentPlayer, ComponentDealer, ComponentHand, ComponentDeck, ComponentDeposit }; use crate::models::traits::{ComponentPlayerDisplay, IDealer}; -use crate::tests::utils::namespace_def; +use crate::tests::utils::{deploy_world, namespace_def}; use crate::tests::integration::test_game::deploy_game; use dojo::model::{ModelStorage, ModelValueStorage}; @@ -53,15 +53,16 @@ pub fn deploy_player(ref world: WorldStorage) -> IPlayerSystemDispatcher { #[test] fn test_join_first_player() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); + let game_addr = starknet::contract_address_const::<0x0ff>(); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Join first player starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", game_addr); // Verify player was added to game - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(game_addr); assert!(game.m_players.len() == 1, "Player should be added to game"); // Verify player component created @@ -74,19 +75,20 @@ fn test_join_first_player() { fn test_join_multiple_players() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); + let game_addr = starknet::contract_address_const::<0x0ff>(); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Join first player starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", game_addr); // Join second player starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); + player_system.join("Player 2", game_addr); // Verify both players added - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(game_addr); assert!(game.m_players.len() == 2, "Both players should be added"); // Verify player order @@ -95,7 +97,7 @@ fn test_join_multiple_players() { } #[test] -#[should_panic(expected: ("Lobby already full", 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ("Lobby full", 'ENTRYPOINT_FAILED'))] fn test_join_full_lobby() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); @@ -103,60 +105,61 @@ fn test_join_full_lobby() { let fourth_caller: ContractAddress = starknet::contract_address_const::<0x0d>(); let fifth_caller: ContractAddress = starknet::contract_address_const::<0x0e>(); let sixth_caller: ContractAddress = starknet::contract_address_const::<0x0f>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); + let game_addr = starknet::contract_address_const::<0x0ff>(); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); // Join first player starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", game_addr); // Join second player starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); + player_system.join("Player 2", game_addr); // Join third player starknet::testing::set_contract_address(third_caller); - player_system.join("Player 3"); + player_system.join("Player 3", game_addr); // Join fourth player starknet::testing::set_contract_address(fourth_caller); - player_system.join("Player 4"); + player_system.join("Player 4", game_addr); // Join fifth player starknet::testing::set_contract_address(fifth_caller); - player_system.join("Player 5"); + player_system.join("Player 5", game_addr); // Verify 5 players are in the game - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(game_addr); assert!(game.m_players.len() == 5, "Should have 5 players"); // Try to join with 6th player - should panic starknet::testing::set_contract_address(sixth_caller); - player_system.join("Player 6"); + player_system.join("Player 6", game_addr); } #[test] fn test_leave_game() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); // Setup game with two players starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - - game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // First player leaves starknet::testing::set_contract_address(first_caller); - player_system.leave(); + player_system.leave(addr); // Verify player removed from game - let game: ComponentGame = world.read_model(world.dispatcher.contract_address); + let game: ComponentGame = world.read_model(addr); assert!(game.m_players.len() == 1, "Player should be removed from game"); assert!(game.m_players.at(0) == @second_caller, "Remaining player should be player 2"); @@ -172,15 +175,19 @@ fn test_leave_game() { } #[test] -#[should_panic(expected: ("Game has not started yet", 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ("Player not found", 'ENTRYPOINT_FAILED'))] fn test_leave_before_game_starts() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); - // Try to leave before game starts + // Try to leave twice starknet::testing::set_contract_address(first_caller); - player_system.leave(); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); + player_system.leave(addr); + player_system.leave(addr); } #[test] @@ -189,20 +196,21 @@ fn test_leave_nonexistent_player() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); let third_caller: ContractAddress = starknet::contract_address_const::<0x0c>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); // Setup game with one player starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Try to leave with non-existent player starknet::testing::set_contract_address(third_caller); - player_system.leave(); + player_system.leave(addr); } #[test] @@ -211,18 +219,19 @@ fn test_join_after_game_starts() { let first_caller: ContractAddress = starknet::contract_address_const::<0x0a>(); let second_caller: ContractAddress = starknet::contract_address_const::<0x0b>(); let third_caller: ContractAddress = starknet::contract_address_const::<0x0c>(); - let mut world: WorldStorage = spawn_test_world([namespace_def()].span()); + let mut world: WorldStorage = deploy_world(); let player_system: IPlayerSystemDispatcher = deploy_player(ref world); - let game_system: IGameSystemDispatcher = deploy_game(ref world); + let (addr, _game_system): (ContractAddress, IGameSystemDispatcher) = deploy_game(ref world); // Setup and start game starknet::testing::set_contract_address(first_caller); - player_system.join("Player 1"); + player_system.join("Player 1", addr); + player_system.set_ready(true, addr); starknet::testing::set_contract_address(second_caller); - player_system.join("Player 2"); - game_system.start(); + player_system.join("Player 2", addr); + player_system.set_ready(true, addr); // Try to join after game started starknet::testing::set_contract_address(third_caller); - player_system.join("Player 3"); + player_system.join("Player 3", addr); } diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index a77ca13..3e2bb64 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -2,7 +2,7 @@ use zktt::{ systems::game::{game_system, IGameSystemDispatcher, IGameSystemDispatcherTrait}, models::components::{ m_ComponentCard, m_ComponentGame, m_ComponentPlayer, m_ComponentDealer, m_ComponentDeck, - m_ComponentHand, m_ComponentDeposit + m_ComponentHand, m_ComponentDeposit, m_ComponentDiscardPile }, models::enums::{EnumGameState}, models::traits::{IDealer, IPlayer} }; @@ -33,6 +33,7 @@ pub fn namespace_def() -> NamespaceDef { TestResource::Model(m_ComponentDeck::TEST_CLASS_HASH.try_into().unwrap()), TestResource::Model(m_ComponentDeposit::TEST_CLASS_HASH.try_into().unwrap()), TestResource::Model(m_ComponentCard::TEST_CLASS_HASH.try_into().unwrap()), + TestResource::Model(m_ComponentDiscardPile::TEST_CLASS_HASH.try_into().unwrap()), TestResource::Contract(game_system::TEST_CLASS_HASH), TestResource::Contract(player_system::TEST_CLASS_HASH), TestResource::Contract(action_system::TEST_CLASS_HASH) From a66ca8ab01d8326452e7d02ac00b9d0cd65dbcd6 Mon Sep 17 00:00:00 2001 From: Nami Reghbati Date: Wed, 27 Nov 2024 15:51:06 -0500 Subject: [PATCH 05/15] chore: Bidnings and manifest updated with new structure and added models. --- bindings/typescript/models.gen.ts | 18 +++++++++--------- manifest_dev.json | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bindings/typescript/models.gen.ts b/bindings/typescript/models.gen.ts index 95073c3..31c63a2 100644 --- a/bindings/typescript/models.gen.ts +++ b/bindings/typescript/models.gen.ts @@ -83,7 +83,7 @@ export interface StructAsset { export interface StructBlockchain { fieldOrder: string[]; m_name: string; - m_bc_type: EnumBlockchainType; + m_bc_type: EnumColor; m_fee: number; m_value: number; } @@ -152,7 +152,7 @@ export interface ComponentDeck { export interface StructBlockchain { fieldOrder: string[]; m_name: string; - m_bc_type: EnumBlockchainType; + m_bc_type: EnumColor; m_fee: number; m_value: number; } @@ -256,7 +256,7 @@ export interface ActionPriorityFee { export interface StructBlockchain { fieldOrder: string[]; m_name: string; - m_bc_type: EnumBlockchainType; + m_bc_type: EnumColor; m_fee: number; m_value: number; } @@ -378,7 +378,7 @@ export interface StructAsset { export interface StructBlockchain { fieldOrder: string[]; m_name: string; - m_bc_type: EnumBlockchainType; + m_bc_type: EnumColor; m_fee: number; m_value: number; } @@ -435,8 +435,8 @@ export interface ComponentPlayerValue { m_in_debt: Option; } -// Type definition for `zktt::models::enums::EnumBlockchainType` enum -export enum EnumBlockchainType { +// Type definition for `zktt::models::enums::EnumColor` enum +export enum EnumColor { Blue, DarkBlue, Gold, @@ -540,7 +540,7 @@ export const schema: ZkttSchemaType = { ActionFiftyOnePercentAttack: { fieldOrder: ['m_owner', 'm_set', 'm_value', 'm_index'], m_owner: "", - m_set: [{ fieldOrder: ['m_name', 'm_bc_type', 'm_fee', 'm_value'], m_name: "", m_bc_type: EnumBlockchainType, m_fee: 0, m_value: 0, }], + m_set: [{ fieldOrder: ['m_name', 'm_bc_type', 'm_fee', 'm_value'], m_name: "", m_bc_type: EnumColor, m_fee: 0, m_value: 0, }], m_value: 0, m_index: 0, }, @@ -553,7 +553,7 @@ export const schema: ZkttSchemaType = { fieldOrder: ['m_players_affected', 'm_blockchain_type_affected', 'm_set_applied', 'm_value', 'm_index'], m_players_affected: EnumPlayerTarget.All, m_blockchain_type_affected: EnumGasFeeType.Any, - m_set_applied: [{ fieldOrder: ['m_name', 'm_bc_type', 'm_fee', 'm_value'], m_name: "", m_bc_type: EnumBlockchainType, m_fee: 0, m_value: 0, }], + m_set_applied: [{ fieldOrder: ['m_name', 'm_bc_type', 'm_fee', 'm_value'], m_name: "", m_bc_type: EnumColor, m_fee: 0, m_value: 0, }], m_value: 0, m_index: 0, }, @@ -579,7 +579,7 @@ export const schema: ZkttSchemaType = { StructBlockchain: { fieldOrder: ['m_name', 'm_bc_type', 'm_fee', 'm_value'], m_name: "", - m_bc_type: EnumBlockchainType.Blue, + m_bc_type: EnumColor.Blue, m_fee: 0, m_value: 0, }, diff --git a/manifest_dev.json b/manifest_dev.json index 648defd..284c3e4 100644 --- a/manifest_dev.json +++ b/manifest_dev.json @@ -1312,7 +1312,7 @@ }, { "type": "enum", - "name": "zktt::models::enums::EnumBlockchainType", + "name": "zktt::models::enums::EnumColor", "variants": [ { "name": "Blue", @@ -1366,7 +1366,7 @@ }, { "name": "m_bc_type", - "type": "zktt::models::enums::EnumBlockchainType" + "type": "zktt::models::enums::EnumColor" }, { "name": "m_fee", @@ -1446,7 +1446,7 @@ }, { "name": "AgainstTwo", - "type": "(zktt::models::enums::EnumBlockchainType, zktt::models::enums::EnumBlockchainType)" + "type": "(zktt::models::enums::EnumColor, zktt::models::enums::EnumColor)" } ] }, From e2bc6f140d35b4043eed17cd626d41fbcd0d02e2 Mon Sep 17 00:00:00 2001 From: Nami Reghbati Date: Wed, 27 Nov 2024 15:55:27 -0500 Subject: [PATCH 06/15] add: Added CI pipeline for tests. --- .github/workflows/build_and_test.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/build_and_test.yaml diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml new file mode 100644 index 0000000..b022edd --- /dev/null +++ b/.github/workflows/build_and_test.yaml @@ -0,0 +1,14 @@ +name: CI + +on: + push: + pull_request: + +jobs: + sozo-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: curl -L https://install.dojoengine.org | bash + - run: /home/runner/.config/.dojo/bin/dojoup + - run: /home/runner/.config/.dojo/bin/sozo test \ No newline at end of file From 9da4ec9ca461163858e64b17070298ac67b45ec3 Mon Sep 17 00:00:00 2001 From: Nami-Webisoft <169168024+Nami-Webisoft@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:03:47 -0500 Subject: [PATCH 07/15] Added CI Badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 443c94f..d877199 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![CI](https://github.com/webisoftSoftware/zktt-contracts/actions/workflows/build_and_test.yaml/badge.svg?branch=ZKTT-3--Add-Missing-Cards)](https://github.com/webisoftSoftware/zktt-contracts/actions/workflows/build_and_test.yaml) ` /////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////// ______ __ __ ______ ______ //////////////////////////////// From a41f6ab07d67c76a7ffe63083203c832d3471734 Mon Sep 17 00:00:00 2001 From: Nami-Webisoft <169168024+Nami-Webisoft@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:04:09 -0500 Subject: [PATCH 08/15] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d877199..12c3b28 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ [![CI](https://github.com/webisoftSoftware/zktt-contracts/actions/workflows/build_and_test.yaml/badge.svg?branch=ZKTT-3--Add-Missing-Cards)](https://github.com/webisoftSoftware/zktt-contracts/actions/workflows/build_and_test.yaml) + ` /////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////// ______ __ __ ______ ______ //////////////////////////////// From 82afdcb80fdf1f349bd8d50531472a6e171bf5ef Mon Sep 17 00:00:00 2001 From: Nami-Webisoft <169168024+Nami-Webisoft@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:04:37 -0500 Subject: [PATCH 09/15] Delete .idea directory --- .idea/workspace.xml | 65 --------------------------------------------- 1 file changed, 65 deletions(-) delete mode 100644 .idea/workspace.xml diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index 01f6583..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - { - "associatedIndex": 0 -} - - - - { - "keyToString": { - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.rust.reset.selective.auto.import": "true", - "git-widget-placeholder": "main", - "last_opened_file_path": "/home/nami/Documents/zKTT", - "node.js.detected.package.eslint": "true", - "node.js.detected.package.tslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "node.js.selected.package.tslint": "(autodetect)", - "nodejs_package_manager_path": "npm", - "org.rust.first.attach.projects": "true", - "settings.editor.selected.configurable": "cairo.settings.AppSettingsConfigurable", - "vue.rearranger.settings.migration": "true" - } -} - - - - - - - 1730883669448 - - - - - - - -