This is a documentation for Board Game Arena: play board games online !

Deck: Difference between revisions

From Board Game Arena
Jump to navigation Jump to search
 
(38 intermediate revisions by 20 users not shown)
Line 1: Line 1:
"Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side.
{{Studio_Framework_Navigation}}
 
"Deck" is one of the most useful component on the PHP side. With "Deck", you can manage the cards in your game on the server side.


Using "deck", you will be able to use the following features without writing a single SQL database request:
Using "deck", you will be able to use the following features without writing a single SQL database request:
* Place cards in pile, shuffle cards, draw cards one by one or many by many.
* Place cards in a pile, shuffle cards, draw cards one by one or many at a time.
* "Auto-reshuffle" discard pile into deck when deck is empty.
* "Auto-reshuffle" the discard pile into the deck when the deck is empty.
* Move cards between different locations: hands of players, the table, ...
* Move cards between different locations: hands of players, the table, etc.




== Using Deck: Hearts example ==
== Using Deck: Hearts example ==


Deck component is massively used in "Hearts" example game - a card game. You can find in "hearts.game.php" that the object "$this->cards" is used many times.
The Deck component is extensively used in the sample ''Hearts'' card game. You will find in "hearts.game.php" that the object "$this->cards" is used many times.


== Deck overview ==
== Deck overview ==
Line 17: Line 19:
=== The 5 properties of each card ===
=== The 5 properties of each card ===


Using Deck component, each card will have 5 properties:
Using the Deck component, each card will have 5 properties:
* '''id''': This is the unique ID of each card.
* '''id''': This is the unique ID of each card.
* '''type''' and '''type_arg''': These two values defines the type of your card (=what sort of card this is?).
* '''type''' and '''type_arg''': These two values define the type of your card (i.e., what sort of card is this?).
* '''location''' and '''location_arg''': These two values defines where is the card at now.
* '''location''' and '''location_arg''': These two values define where the card is at now.
 
The id, type, and type_arg properties are constants throughout the game. location and location_arg change when your cards move from one place to another in the game area.
 
'''id''' is the unique ID of each card. Two cards cannot have the same ID. IDs are generated automatically by the Deck component when you create cards during the Setup phase of your game.


id, type and type_arg properties are constants during the game. location and location_arg are changing when your cards are moving from places to places on the game area.
'''type''' and '''type_arg''' defines the type of your card.


'''id''' is the unique ID of each card. Two cards can't get the same ID. IDs are generated automatically by the Deck component when you create cards during the Setup phase of your game.
'''type''' is a short string, and '''type_arg''' is an integer.


'''type''' and '''type_arg''' defines the type of your card. '''type''' is a short string, and '''type_arg''' is an integer. You can use these two values as you want to make sure you will be able to identify the different cards of the game. See usage of "type" and "type_arg" below.
You can use these two values as you like to make sure you will be able to identify the different cards in the game. See usage of "type" and "type_arg" below.


Examples of usage of "type" and "type_arg":
Examples of usage of "type" and "type_arg":
* In "Hearts", "type" is the color of the card (1 to 4) and "type_arg" is the value of the card (1, 2, ... 10, J, Q, K).
* In ''Hearts'', "type" represents the color (suite) of the card (1 to 4) and "type_arg" is the value of the card (1, 2, ... 10, J, Q, K).
* In "Seasons", "type" is the type of the card (ex: 1 is Amulet of Air, 2 is Amulet of Fire, etc...). type_arg is not used.
* In ''Seasons'', "type" represents the type of the card (e.g., 1 is Amulet of Air, 2 is Amulet of Fire, etc...). type_arg is not used.
* In "Takenoko", a Deck component is used for objective cards. "type" is the kind of objective (irrigation/panda/plot) and "type_arg" is the ID of the specific objective to realize (ex: "a green bamboo x4"). Note that a second Deck component is used in Takenoko to manage the "garden plot" pile.
* In ''Takenoko'', a Deck component is used for objective cards. "type" is the kind of objective (irrigation/panda/plot) and "type_arg" is the ID of the specific objective to realize (e.g., "green bamboo x4"). Note that a second Deck component is used in ''Takenoko'' to manage the "garden plot" pile.


'''location''' and '''location_arg''' define where a card is at now. '''location''' is a short string, and '''location_arg''' is an integer.


'''location''' and '''location_arg''' defines where is the card at now. '''location''' is a short string, and '''location_arg''' is an integer.
You can use 'location' and 'location_arg' as you like, to move your card within the game area.


You can use 'location' and 'location_arg' as you want, to move your card on the game area. Although, there are 3 special 'location' that Deck manage specifically. You can choose to use - or not to use - these locations depending of your needs:
There are 3 special 'location' values that Deck manages automatically. You can choose to use these locations or not, depending on your needs:
* 'deck': in 'deck' location, cards are placed face down in a pile and are drawn during the game. 'location_arg' is used to specify where the card is in the deck pile (the card with the biggest location_arg is the next to be drawn).
* 'deck': the 'deck' location is a standard draw deck. Cards are placed face down in a stack and are drawn in sequential order during the game. 'location_arg' is used to specify where the card is located within the stack (the card with the highest location_arg value is the next to be drawn).
* 'hand': in 'hand' location, cards are in the hand of a player. 'location_arg' is set to the ID of this player.
* 'hand': the 'hand' location represents cards in a player's hand. 'location_arg' is set to the ID of each player.
* 'discard': in 'discard' location, cards are discarded, and are ready to be shuffled into the deck if needed (see "autoreshuffle").
* 'discard': the 'discard' location is used for discard piles. Card in 'discard' may be reshuffled into the deck if needed (see "autoreshuffle").




Tips: using Deck component, you are going to use generic properties ("location", "type_arg",...) for specific purposes of your game. Thus, during the design step before realizing your game, take 2 minutes to write down what is the meaning of each of this generic properties in the context of your game.
Tips: using the Deck component, you will use generic properties ("location", "type_arg",...) for specific purposes in your game. Thus, during the design step before realizing your game, take a few minutes to write down the exact meaning of each of these generic properties in the context of your game.


=== Create a new Deck component ===
=== Create a new Deck component ===


For each Deck component in your game, you need to create a dedicated table in database. This table has a standard format. In practical, if you want to have a Deck component named "card", you just have to copy/paste the following in your "dbmodel.sql":
For each Deck component in your game, you need to create a dedicated table in the SQL database. This table has a standard format. In practice, if you just want to have a Deck component named "card", you can copy/paste the following into your "dbmodel.sql" file:


<pre>
<pre>
Line 59: Line 66:
</pre>
</pre>


Once you did this (and restart your game), you can declare your Deck component in your PHP code in your class constructor. For Hearts for example, I added to "Hearts()" method:
Note: the database schema of this table does not have to be exactly what is listed above. You can increase the size of the fields or add more fields. For additional fields
you just have to do manual queries.
 
In particular, if you are going to have deck locations specific to individual players, you may wish to use their player IDs in the card_location field. Those IDs can be 8+ characters long, leaving only 8 characters for the rest of the name if you use the varchar(16). If you exceed the size of the field, it will get silently truncated, which can be very difficult to troubleshoot!
 
Once you have done this (and restarted your game), you can declare the Deck component in your PHP code in your class constructor. For ''Hearts'' for example, I added to the "Hearts()" method:


<pre>
<pre>
         $this->cards = self::getNew( "module.common.deck" );
         $this->cards = $this->getNew( "module.common.deck" );
         $this->cards->init( "card" );
         $this->cards->init( "card" );
</pre>
</pre>


Note that we specify "card" here, the name of our previously created table. It means you can create several "Deck" with several tables. Most of the time this is unuseful: a Deck component should manage all objects of the same kind (ex: all cards of the game).
Note that we specify "card" here: the name of our previously created table. This means you can create several "Deck" components with multiple tables:


Afterwards, we can initialize your "Deck" by creating all the cards of the game. Generally, this is done only once during the game, during the "setupNewGame" method.
<pre>
        $this->firstKindCards = $this->getNew( "module.common.deck" );
        $this->firstKindCards ->init( "first_kind_card" );
        $this->secondKindCards = $this->getNew( "module.common.deck" );
        $this->secondKindCards ->init( "second_kind_card" );
</pre>


"Deck" component provides you a fast way to initialize all your cards at once: createCards. Here is how it is used for "Hearts":
Most of the time this is not useful; a Deck component should manage all objects of the same kind (i.e., all cards in the game).
<pre>
Note that you need to create a table for each "Deck", table name should be "first_kind_card" but the fields must remain "card_id", "card_type" and so on.
 
Afterwards, we can initialize your "Deck" by creating all the cards of the game. Generally, this is done only once during the game, in the "setupNewGame" method.
 
The "Deck" component provides a fast way to initialize all your cards at once: createCards. Here is how it is used for "Hearts":<pre>
         // Create cards
         // Create cards
         $cards = array();
         $cards = array();
Line 85: Line 106:
</pre>
</pre>


As you can see, "createCards" takes a description of all cards of the game. For each type of card, you have to specify its "type", "type_arg" and the number of card to create. "createCards" create all cards and place them into the "deck" location (as specified in the second argument).
As you can see, "createCards" takes a description of all cards of the game. For each type of card, you have to specify its "type", "type_arg" and the number of cards to create ("nbr"). "createCards" create all cards and place them into the "deck" location (as specified in the second argument).


Now, you are ready to use "Deck"!
Now, you are ready to use "Deck"!
Line 122: Line 143:
     // Deal 13 cards to each players
     // Deal 13 cards to each players
     // Create deck, shuffle it and give 13 initial cards
     // Create deck, shuffle it and give 13 initial cards
     $players = self::loadPlayersBasicInfos();
     $players = $this->loadPlayersBasicInfos();
     foreach( $players as $player_id => $player )
     foreach( $players as $player_id => $player )
     {
     {
Line 128: Line 149:
            
            
         // Notify player about his cards
         // Notify player about his cards
         self::notifyPlayer( $player_id, 'newHand', '', array(  
         $this->notifyPlayer( $player_id, 'newHand', '', array(  
             'cards' => $cards
             'cards' => $cards
         ) );
         ) );
Line 150: Line 171:


Usually, init is called in your game constructor.
Usually, init is called in your game constructor.
HINT: Create the deck object $this->getNew and do the init call in the constructor or you will get "$this->cards" as an invalid reference later.


Example with Hearts:
Example with Hearts:
Line 158: Line 181:
         (...)
         (...)
          
          
         $this->cards = self::getNew( "module.common.deck" );
         $this->cards = $this->getNew( "module.common.deck" );
         $this->cards->init( "card" );
         $this->cards->init( "card" );
}
}
Line 175: Line 198:


   $cards = array(
   $cards = array(
         array( 'type' => 1, 'type_arg' => 99, nbr' => 1 ),
         array( 'type' => 1, 'type_arg' => 99, 'nbr' => 1 ),
         array( 'type' => 2, 'type_arg' => 12, nbr' => 4 ),
         array( 'type' => 2, 'type_arg' => 12, 'nbr' => 4 ),
         array( 'type' => 3, 'type_arg' => 33, nbr' => 2 )
         array( 'type' => 3, 'type_arg' => 33, 'nbr' => 2 )
         ...
         ...
   );
   );
</pre>
</pre>


Note: During the "createCards" process, Deck generate unique IDs for all card items.
Note: During the "createCards" process, Deck generates unique IDs for all card items.


Note: createCards is optimized to create a lot of cards at once. Do not use it to create cards one by one.
Note: createCards is optimized to create a lot of cards at once. Do not use it to create cards one by one.


If "location" and "location_arg" arguments are not set, newly created cards are placed in the "deck" location. If "location" (and optionally location_arg) is specified, cards are created for this specific location.
If "$location" and "$location_arg" arguments are not set, newly created cards are placed in the "deck" location. If "location" (and optionally location_arg) is specified, cards are created for this specific location.
 
Note: 'location' and 'location_arg' can not be set individually, it does not read these values from the passed array of cards.
 
HINT: Be sure to do the createCards in setupNewGame. Doing createCards in the constructor will throw database errors.


=== Card standard format ===
=== Card standard format ===
Line 209: Line 236:


Return the card picked or "null" if there are no more card in given location.
Return the card picked or "null" if there are no more card in given location.
The return value is an array of the card data elements (id, type, type_arg...) for that card.


This method supports auto-reshuffle (see "auto-reshuffle" below).
This method supports auto-reshuffle (see "auto-reshuffle" below).
Line 216: Line 245:
Pick "$nbr" cards from a "pile" location (ex: "deck") and place them in the "hand" of specified player.
Pick "$nbr" cards from a "pile" location (ex: "deck") and place them in the "hand" of specified player.


Return an array with the cards picked, or "null" if there are no more card in given location.
Return an array with the cards picked (indexed by the card ID), or "null" if there are no more card in given location.


Note that the number of cards picked can be less than "$nbr" in case there are not enough cards in the pile location.
Note that the number of cards picked can be less than "$nbr" in case there are not enough cards in the pile location.
Line 226: Line 255:
This method is similar to 'pickCard', except that you can pick a card for any sort of location and not only the "hand" location.
This method is similar to 'pickCard', except that you can pick a card for any sort of location and not only the "hand" location.


_ from_location is the "pile" style location from where you are picking a card.
* from_location is the "pile" style location from where you are picking a card.
_ to_location is the location where you will place the card picked.
* to_location is the location where you will place the card picked.
_ if "location_arg" is specified, the card picked will be set with this "location_arg".
* if "location_arg" is specified, the card picked will be set with this "location_arg".


This method supports auto-reshuffle (see "auto-reshuffle" below).
This method supports auto-reshuffle (see "auto-reshuffle" below).
Line 236: Line 265:
This method is similar to 'pickCards', except that you can pick cards for any sort of location and not only the "hand" location.
This method is similar to 'pickCards', except that you can pick cards for any sort of location and not only the "hand" location.


_ from_location is the "pile" style location from where you are picking some cards.
* from_location is the "pile" style location from where you are picking some cards.
_ to_location is the location where you will place the cards picked.
* to_location is the location where you will place the cards picked.
_ if "location_arg" is specified, the cards picked will be set with this "location_arg".
* if "location_arg" is specified, the cards picked will be set with this "location_arg".
_ if "no_deck_reform" is set to "true", the auto-reshuffle feature is disabled during this method call.
* if "no_deck_reform" is set to "true", the auto-reshuffle feature is disabled during this method call.


This method supports auto-reshuffle (see "auto-reshuffle" below).
This method supports auto-reshuffle (see "auto-reshuffle" below).
Line 249: Line 278:
Move the specific card to given location.
Move the specific card to given location.


_ card_id: ID of the card to move.
* card_id: ID of the card to move.
_ location: location where to move the card.
* location: location where to move the card.
_ location_arg: if specified, location_arg where to move the card. If not specified "location_arg" will be set to 0.
* location_arg: if specified, location_arg where to move the card. If not specified "location_arg" will be set to 0.




'''moveCards( $cards, $location, $location_arg )'''
'''moveCards( $cards, $location, $location_arg=0 )'''


Move the specific cards to given location.
Move the specific cards to given location.


_ cards: an array of IDs of cards to move.
* cards: an array of IDs of cards to move.
_ location: location where to move the cards.
* location: location where to move the cards.
_ location_arg: if specified, location_arg where to move the cards. If not specified "location_arg" will be set to 0.
* location_arg: if specified, location_arg where to move the cards. If not specified "location_arg" will be set to 0.


'''insertCard( $card_id, $location, $location_arg )'''
'''insertCard( $card_id, $location, $location_arg )'''
Line 272: Line 301:
'''insertCardOnExtremePosition( $card_id, $location, $bOnTop )'''
'''insertCardOnExtremePosition( $card_id, $location, $bOnTop )'''


Move a card on top or at bottom of given "pile" type location.
Move a card on top or at bottom of given "pile" type location. (Lower numbers: bottom of the deck. Higher numbers: top of the deck.)
 
(note: Filling an empty location this way with N cards creates "location_arg"s from 1 to N if "$bOnTop" is true and -1 to -N if "$bOnTop" is false. This can cause off-by-one errors for code intended to run on a deck generated by "shuffle( $location )" which generates "location_arg"s from 0 to N - 1.)


'''moveAllCardsInLocation(  $from_location, $to_location, $from_location_arg=null, $to_location_arg=0 )'''
'''moveAllCardsInLocation(  $from_location, $to_location, $from_location_arg=null, $to_location_arg=0 )'''
Line 278: Line 309:
Move all cards in specified "from" location to given location.
Move all cards in specified "from" location to given location.


_ from_location: where to take the cards
* from_location: where to take the cards. If null, cards from all locations will be move.
_ to_location: where to put the cards
* to_location: where to put the cards
_ from_location (optional): if specified, only cards with given "location_arg" are moved.
* from_location_arg (optional): if specified, only cards with given "location_arg" are moved.
_ to_location (optional): if specified, cards moved "location_arg" is set to given value. Otherwise location_arg is set to zero.
* to_location_arg (optional): if specified, cards moved "location_arg" is set to given value. Otherwise "location_arg" is set to 0.


Note: if you want to keep "location_arg" untouched, you should use "moveAllCardsInLocationKeepOrder" below.
Note: if you want to keep "location_arg" untouched, you should use "moveAllCardsInLocationKeepOrder" below.
Line 307: Line 338:
Get specific cards information.
Get specific cards information.


cards_array is an array of cards ID.
$cards_array is an array of card IDs.


If some cards are not found or if some cards IDs are specified multiple times, the method throws an (unexpected) Exception.
If some cards are not found or if some card IDs are specified multiple times, the method throws an (unexpected) Exception.


'''getCardsInLocation( $location, $location_arg = null, $order_by = null )'''
'''getCardsInLocation( $location, $location_arg = null, $order_by = null )'''
Line 315: Line 346:
Get all cards in specific location, as an array. Return an empty array if the location is empty.
Get all cards in specific location, as an array. Return an empty array if the location is empty.


_ location (string): the location where to get the cards.
* location (string): the location where to get the cards.
_ location_arg (optional): if specified, return only cards with the specified "location_arg".
* location_arg (optional): if specified, return only cards with the specified "location_arg".
_ order_by (optional): if specified, returned cards are ordered by the given database field. Example: "card_id" or "card_type".
* order_by (optional): if specified, returned cards are ordered by the given database field. Example: "card_id" or "card_type".
Using the "order_by" parameter changes the resulting array. Without parameter you get an associative array with the "card_id", with the paramter you get a simple indexed array.


'''countCardInLocation( $location, $location_arg=null )'''
'''countCardInLocation( $location, $location_arg=null )'''
Line 323: Line 355:
Return the number of cards in specified location.
Return the number of cards in specified location.


_ location (string): the location where to count the cards.
* location (string): the location where to count the cards.
_ location_arg (optional): if specified, count only cards with the specified "location_arg".
* location_arg (optional): if specified, count only cards with the specified "location_arg".


'''countCardsInLocations()'''
'''countCardsInLocations()'''
Line 399: Line 431:
Return an array of cards, or an empty array if there is no cards of the specified type.
Return an array of cards, or an empty array if there is no cards of the specified type.


_ type: the type of cards
* type: the type of cards
_ type_arg: if specified, return only cards with the specified "type_arg".
* type_arg: if specified, return only cards with the specified "type_arg".
 
'''getCardsOfTypeInLocation( $type, $type_arg=null, $location, $location_arg = null )
 
Get all cards of a specific type in a specific location (rarely used).
 
Return an array of cards, or an empty array if there is no cards of the specified type.
 
* type: the type of cards
* type_arg: if specified, return only cards with the specified "type_arg".
* location (string): the location where to get the cards.
* location_arg (optional): if specified, return only cards with the specified "location_arg".


=== Shuffling ===
=== Shuffling ===
Line 410: Line 453:
Shuffle only works on locations where cards are on a "pile" (ex: "deck").
Shuffle only works on locations where cards are on a "pile" (ex: "deck").


Please note that all "location_arg" will be reseted to reflect the new order of the cards in the pile.
Please note that all "location_arg" will be reset to reflect the new order of the cards in the pile.
 
=== Auto-reshuffle ===
 
To enable auto-reshuffle you must do "<code>$this->cards->autoreshuffle = true</code>" during the setup of the component (often in the ''_construct'' function when you ''init()'' the Deck object).
 
Every time a card must be retrieved from the "deck" location, if it is empty the "discard" location will be automatically reshuffled into the "deck" location.
 
If you need to notify players when the deck is shuffled, you can setup a callback method using this feature: <pre>$this->cards->autoreshuffle_trigger = array('obj' => $this, 'method' => 'deckAutoReshuffle');</pre>
 
If you need to use other locations than "deck" and "discard" for auto-reshuffle feature, you can configure it this way: <pre>$this->cards->autoreshuffle_custom = array('deck' => 'discard');</pre> (replace 'deck' and 'discard' with your custom locations).
 
[[Category:Studio]]

Latest revision as of 14:19, 17 September 2024


Game File Reference



Useful Components

Official

  • Deck: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...).
  • Draggable: a JS component to manage drag'n'drop actions.
  • Counter: a JS component to manage a counter that can increase/decrease (ex: player's score).
  • ExpandableSection: a JS component to manage a rectangular block of HTML than can be displayed/hidden.
  • Scrollmap: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games).
  • Stock: a JS component to manage and display a set of game elements displayed at a position.
  • Zone: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop).

Undocumented component (if somebody knows please help with docs)

  • Wrapper: a JS component to wrap a <div> element around its child, even if these elements are absolute positioned.

Unofficial



Game Development Process



Guides for Common Topics



Miscellaneous Resources

"Deck" is one of the most useful component on the PHP side. With "Deck", you can manage the cards in your game on the server side.

Using "deck", you will be able to use the following features without writing a single SQL database request:

  • Place cards in a pile, shuffle cards, draw cards one by one or many at a time.
  • "Auto-reshuffle" the discard pile into the deck when the deck is empty.
  • Move cards between different locations: hands of players, the table, etc.


Using Deck: Hearts example

The Deck component is extensively used in the sample Hearts card game. You will find in "hearts.game.php" that the object "$this->cards" is used many times.

Deck overview

With Deck component, you manage all cards of your game.

The 5 properties of each card

Using the Deck component, each card will have 5 properties:

  • id: This is the unique ID of each card.
  • type and type_arg: These two values define the type of your card (i.e., what sort of card is this?).
  • location and location_arg: These two values define where the card is at now.

The id, type, and type_arg properties are constants throughout the game. location and location_arg change when your cards move from one place to another in the game area.

id is the unique ID of each card. Two cards cannot have the same ID. IDs are generated automatically by the Deck component when you create cards during the Setup phase of your game.

type and type_arg defines the type of your card.

type is a short string, and type_arg is an integer.

You can use these two values as you like to make sure you will be able to identify the different cards in the game. See usage of "type" and "type_arg" below.

Examples of usage of "type" and "type_arg":

  • In Hearts, "type" represents the color (suite) of the card (1 to 4) and "type_arg" is the value of the card (1, 2, ... 10, J, Q, K).
  • In Seasons, "type" represents the type of the card (e.g., 1 is Amulet of Air, 2 is Amulet of Fire, etc...). type_arg is not used.
  • In Takenoko, a Deck component is used for objective cards. "type" is the kind of objective (irrigation/panda/plot) and "type_arg" is the ID of the specific objective to realize (e.g., "green bamboo x4"). Note that a second Deck component is used in Takenoko to manage the "garden plot" pile.

location and location_arg define where a card is at now. location is a short string, and location_arg is an integer.

You can use 'location' and 'location_arg' as you like, to move your card within the game area.

There are 3 special 'location' values that Deck manages automatically. You can choose to use these locations or not, depending on your needs:

  • 'deck': the 'deck' location is a standard draw deck. Cards are placed face down in a stack and are drawn in sequential order during the game. 'location_arg' is used to specify where the card is located within the stack (the card with the highest location_arg value is the next to be drawn).
  • 'hand': the 'hand' location represents cards in a player's hand. 'location_arg' is set to the ID of each player.
  • 'discard': the 'discard' location is used for discard piles. Card in 'discard' may be reshuffled into the deck if needed (see "autoreshuffle").


Tips: using the Deck component, you will use generic properties ("location", "type_arg",...) for specific purposes in your game. Thus, during the design step before realizing your game, take a few minutes to write down the exact meaning of each of these generic properties in the context of your game.

Create a new Deck component

For each Deck component in your game, you need to create a dedicated table in the SQL database. This table has a standard format. In practice, if you just want to have a Deck component named "card", you can copy/paste the following into your "dbmodel.sql" file:

CREATE TABLE IF NOT EXISTS `card` (
  `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `card_type` varchar(16) NOT NULL,
  `card_type_arg` int(11) NOT NULL,
  `card_location` varchar(16) NOT NULL,
  `card_location_arg` int(11) NOT NULL,
  PRIMARY KEY (`card_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Note: the database schema of this table does not have to be exactly what is listed above. You can increase the size of the fields or add more fields. For additional fields you just have to do manual queries.

In particular, if you are going to have deck locations specific to individual players, you may wish to use their player IDs in the card_location field. Those IDs can be 8+ characters long, leaving only 8 characters for the rest of the name if you use the varchar(16). If you exceed the size of the field, it will get silently truncated, which can be very difficult to troubleshoot!

Once you have done this (and restarted your game), you can declare the Deck component in your PHP code in your class constructor. For Hearts for example, I added to the "Hearts()" method:

        $this->cards = $this->getNew( "module.common.deck" );
        $this->cards->init( "card" );

Note that we specify "card" here: the name of our previously created table. This means you can create several "Deck" components with multiple tables:

        $this->firstKindCards = $this->getNew( "module.common.deck" );
        $this->firstKindCards ->init( "first_kind_card" );
        $this->secondKindCards = $this->getNew( "module.common.deck" );
        $this->secondKindCards ->init( "second_kind_card" );

Most of the time this is not useful; a Deck component should manage all objects of the same kind (i.e., all cards in the game). Note that you need to create a table for each "Deck", table name should be "first_kind_card" but the fields must remain "card_id", "card_type" and so on.

Afterwards, we can initialize your "Deck" by creating all the cards of the game. Generally, this is done only once during the game, in the "setupNewGame" method.

The "Deck" component provides a fast way to initialize all your cards at once: createCards. Here is how it is used for "Hearts":

        // Create cards
        $cards = array();
        foreach( $this->colors as  $color_id => $color ) // spade, heart, diamond, club
        {
            for( $value=2; $value<=14; $value++ )   //  2, 3, 4, ... K, A
            {
                $cards[] = array( 'type' => $color_id, 'type_arg' => $value, 'nbr' => 1);
            }
        }

        $this->cards->createCards( $cards, 'deck' );

As you can see, "createCards" takes a description of all cards of the game. For each type of card, you have to specify its "type", "type_arg" and the number of cards to create ("nbr"). "createCards" create all cards and place them into the "deck" location (as specified in the second argument).

Now, you are ready to use "Deck"!

Simple examples using Deck

(Most examples are from "Hearts" game)

     // In "getAllDatas', we need to send to the current player all the cards he has in hand:
     $result['hand'] = $this->cards->getCardsInLocation( 'hand', $player_id );
     // At some time we want to check if all the cards (52) are in player's hands:
     if( $this->cards->countCardInLocation( 'hand' ) == 52 )
           // do something
     // When a player plays a card in front of him on the table:
     $this->cards->moveCard( $card_id, 'cardsontable', $player_id );

     // Note the use of the custom location 'cardsontable' here to keep track of cards on the table.


     // This is a new hand: let's gather all cards from everywhere in the deck:
     $this->cards->moveAllCardsInLocation( null, "deck" );

     // And then shuffle the deck
     $this->cards->shuffle( 'deck' );

     // And then deal 13 cards to each player
     // Deal 13 cards to each players
     // Create deck, shuffle it and give 13 initial cards
     $players = $this->loadPlayersBasicInfos();
     foreach( $players as $player_id => $player )
     {
        $cards = $this->cards->pickCards( 13, 'deck', $player_id );
           
        // Notify player about his cards
        $this->notifyPlayer( $player_id, 'newHand', '', array( 
            'cards' => $cards
         ) );
     }  

     // Note the use of "notifyPlayer" instead of "notifyAllPlayers": new cards is a private information ;)  

Deck component reference

Initializing Deck component

init( $table_name )

Initialize the Deck component.

Argument:

  • table_name: name of the DB table used by this Deck component.

Must be called before any other Deck method.

Usually, init is called in your game constructor.

HINT: Create the deck object $this->getNew and do the init call in the constructor or you will get "$this->cards" as an invalid reference later.

Example with Hearts:

	function Hearts( )
	{
        (...)
        
        $this->cards = $this->getNew( "module.common.deck" );
        $this->cards->init( "card" );
	}

createCards( $cards, $location='deck', $location_arg=null )

Create card items in your deck component. Usually, all card items are created once, during the setup phase of the game.

"cards" describe all cards that need to be created. "cards" is an array with the following format:

   // Create 1 card of type "1" with type_arg=99,
   //  and 4 cards of type "2" with type_arg=12,
   //  and 2 cards of type "3" with type_arg=33

   $cards = array(
        array( 'type' => 1, 'type_arg' => 99, 'nbr' => 1 ),
        array( 'type' => 2, 'type_arg' => 12, 'nbr' => 4 ),
        array( 'type' => 3, 'type_arg' => 33, 'nbr' => 2 )
        ...
   );

Note: During the "createCards" process, Deck generates unique IDs for all card items.

Note: createCards is optimized to create a lot of cards at once. Do not use it to create cards one by one.

If "$location" and "$location_arg" arguments are not set, newly created cards are placed in the "deck" location. If "location" (and optionally location_arg) is specified, cards are created for this specific location.

Note: 'location' and 'location_arg' can not be set individually, it does not read these values from the passed array of cards.

HINT: Be sure to do the createCards in setupNewGame. Doing createCards in the constructor will throw database errors.

Card standard format

When Deck component methods are returning one or several cards, the following format is used:

array(
   'id' => ..,          // the card ID
   'type' => ..,        // the card type
   'type_arg' => ..,    // the card type argument
   'location' => ..,    // the card location
   'location_arg' => .. // the card location argument
);

Picking cards

pickCard( $location, $player_id )

Pick a card from a "pile" location (ex: "deck") and place it in the "hand" of specified player.

Return the card picked or "null" if there are no more card in given location.

The return value is an array of the card data elements (id, type, type_arg...) for that card.

This method supports auto-reshuffle (see "auto-reshuffle" below).

pickCards( $nbr, $location, $player_id )

Pick "$nbr" cards from a "pile" location (ex: "deck") and place them in the "hand" of specified player.

Return an array with the cards picked (indexed by the card ID), or "null" if there are no more card in given location.

Note that the number of cards picked can be less than "$nbr" in case there are not enough cards in the pile location.

This method supports auto-reshuffle (see "auto-reshuffle" below). In case there are not enough cards in the pile, all remaining cards are picked first, then the auto-reshuffle is triggered, then the other cards are picked.

pickCardForLocation( $from_location, $to_location, $location_arg=0 )

This method is similar to 'pickCard', except that you can pick a card for any sort of location and not only the "hand" location.

  • from_location is the "pile" style location from where you are picking a card.
  • to_location is the location where you will place the card picked.
  • if "location_arg" is specified, the card picked will be set with this "location_arg".

This method supports auto-reshuffle (see "auto-reshuffle" below).

pickCardsForLocation( $nbr, $from_location, $to_location, $location_arg=0, $no_deck_reform=false )

This method is similar to 'pickCards', except that you can pick cards for any sort of location and not only the "hand" location.

  • from_location is the "pile" style location from where you are picking some cards.
  • to_location is the location where you will place the cards picked.
  • if "location_arg" is specified, the cards picked will be set with this "location_arg".
  • if "no_deck_reform" is set to "true", the auto-reshuffle feature is disabled during this method call.

This method supports auto-reshuffle (see "auto-reshuffle" below).

Moving cards

moveCard( $card_id, $location, $location_arg=0 )

Move the specific card to given location.

  • card_id: ID of the card to move.
  • location: location where to move the card.
  • location_arg: if specified, location_arg where to move the card. If not specified "location_arg" will be set to 0.


moveCards( $cards, $location, $location_arg=0 )

Move the specific cards to given location.

  • cards: an array of IDs of cards to move.
  • location: location where to move the cards.
  • location_arg: if specified, location_arg where to move the cards. If not specified "location_arg" will be set to 0.

insertCard( $card_id, $location, $location_arg )

Move a card to a specific "pile" location where card are ordered.

If location_arg place is already taken, increment all cards after location_arg in order to insert new card at this precise location.

(note: insertCardOnExtremePosition method below is more useful in most of the case)

insertCardOnExtremePosition( $card_id, $location, $bOnTop )

Move a card on top or at bottom of given "pile" type location. (Lower numbers: bottom of the deck. Higher numbers: top of the deck.)

(note: Filling an empty location this way with N cards creates "location_arg"s from 1 to N if "$bOnTop" is true and -1 to -N if "$bOnTop" is false. This can cause off-by-one errors for code intended to run on a deck generated by "shuffle( $location )" which generates "location_arg"s from 0 to N - 1.)

moveAllCardsInLocation( $from_location, $to_location, $from_location_arg=null, $to_location_arg=0 )

Move all cards in specified "from" location to given location.

  • from_location: where to take the cards. If null, cards from all locations will be move.
  • to_location: where to put the cards
  • from_location_arg (optional): if specified, only cards with given "location_arg" are moved.
  • to_location_arg (optional): if specified, cards moved "location_arg" is set to given value. Otherwise "location_arg" is set to 0.

Note: if you want to keep "location_arg" untouched, you should use "moveAllCardsInLocationKeepOrder" below.

moveAllCardsInLocationKeepOrder( $from_location, $to_location )

Move all cards in specified "from" location to given "to" location. This method does not modify the "location_arg" of cards.

playCard( $card_id )

Move specified card at the top of the "discard" location.

Note: this is an alias for: insertCardOnExtremePosition( $card_id, "discard", true )

Get cards informations

getCard( $card_id )

Get specific card information.

Return null if this card is not found.

getCards( $cards_array )

Get specific cards information.

$cards_array is an array of card IDs.

If some cards are not found or if some card IDs are specified multiple times, the method throws an (unexpected) Exception.

getCardsInLocation( $location, $location_arg = null, $order_by = null )

Get all cards in specific location, as an array. Return an empty array if the location is empty.

  • location (string): the location where to get the cards.
  • location_arg (optional): if specified, return only cards with the specified "location_arg".
  • order_by (optional): if specified, returned cards are ordered by the given database field. Example: "card_id" or "card_type".

Using the "order_by" parameter changes the resulting array. Without parameter you get an associative array with the "card_id", with the paramter you get a simple indexed array.

countCardInLocation( $location, $location_arg=null )

Return the number of cards in specified location.

  • location (string): the location where to count the cards.
  • location_arg (optional): if specified, count only cards with the specified "location_arg".

countCardsInLocations()

Return the number of cards in each location of the game.

The method returns an associative array with the format "location" => "number of cards".

Example:

  array(
    'deck' => 12,
    'hand' => 21,
    'discard' => 54,
    'ontable' => 3
  );

countCardsByLocationArgs( $location )

Return the number of cards in each "location_arg" for the given location.

The method returns an associative array with the format "location_arg" => "number of cards".

Example: count the number of cards in each player's hand:

    countCardsByLocationArgs( 'hand' );
    
    // Result:
    array(
        122345 => 5,    // player 122345 has 5 cards in hand
        123456 => 4     // and player 123456 has 4 cards in hand
    );


getPlayerHand( $player_id )

Get all cards in given player hand.

Note: This is an alias for: getCardsInLocation( "hand", $player_id )

getCardOnTop( $location )

Get the card on top of the given ("pile" style) location, or null if the location is empty.

Note that the card pile won't be "auto-reshuffled" if there is no more card available.

getCardsOnTop( $nbr, $location )

Get the "$nbr" cards on top of the given ("pile" style) location.

The method return an array with at most "$nbr" elements (or a void array if there is no card in this location).

Note that the card pile won't be "auto-reshuffled" if there is not enough cards available.

getExtremePosition( $bGetMax ,$location )

(rarely used)

Get the position of cards at the top of the given location / at the bottom of the given location.

Of course this method works only on location in "pile" where you are using "location_arg" to specify the position of each card (example: "deck" location).

If bGetMax=true, return the location of the top card of the pile.

If bGetMax=false, return the location of the bottom card of the pile.

getCardsOfType( $type, $type_arg=null )

Get all cards of a specific type (rarely used).

Return an array of cards, or an empty array if there is no cards of the specified type.

  • type: the type of cards
  • type_arg: if specified, return only cards with the specified "type_arg".

getCardsOfTypeInLocation( $type, $type_arg=null, $location, $location_arg = null )

Get all cards of a specific type in a specific location (rarely used).

Return an array of cards, or an empty array if there is no cards of the specified type.

  • type: the type of cards
  • type_arg: if specified, return only cards with the specified "type_arg".
  • location (string): the location where to get the cards.
  • location_arg (optional): if specified, return only cards with the specified "location_arg".

Shuffling

shuffle( $location )

Shuffle all cards in specific location.

Shuffle only works on locations where cards are on a "pile" (ex: "deck").

Please note that all "location_arg" will be reset to reflect the new order of the cards in the pile.

Auto-reshuffle

To enable auto-reshuffle you must do "$this->cards->autoreshuffle = true" during the setup of the component (often in the _construct function when you init() the Deck object).

Every time a card must be retrieved from the "deck" location, if it is empty the "discard" location will be automatically reshuffled into the "deck" location.

If you need to notify players when the deck is shuffled, you can setup a callback method using this feature:

$this->cards->autoreshuffle_trigger = array('obj' => $this, 'method' => 'deckAutoReshuffle');

If you need to use other locations than "deck" and "discard" for auto-reshuffle feature, you can configure it this way:

$this->cards->autoreshuffle_custom = array('deck' => 'discard');

(replace 'deck' and 'discard' with your custom locations).