I have a simple card game (using 52 cards - no jokers) that I want to randomly pick 1 card at a time until the winning card is chosen.
I have the following array:
$cards = array(
'diamond' => array(
'A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K'
),
'heart' => array(
'A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K'
),
'club' => array(
'A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K'
),
'spades' => array(
'A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K'
),
);
As you can see, this array is sorted. I would like to shuffle them using the PHP function shuffle($cards);
but it didn't work.
What can I do in order to get this suffled?
I'd make classes Deck
and Card
. Card would hold what suit it is, as well as its 'number'. Then you can call method shuffle
on Deck
, which is simply an array of Cards. This way, all of the cards are sorted independent of their suit.
[Update] Features:
__construct
and reset
. They now use a new function, createDeck
, which uses createSuit
to ease the creation process.jsonSerialize
will return the data to be serialized. In PHP 5.4 and above you can call json_encode
on the object if it implements this interface. Before then, you can call json_encode($deck->jsonSerialize())
.count($deck)
to get the size of the deck.deck[0]->suit
would return the suit of the first card of the deck.foreach
loop.<?php
if (!interface_exists('JsonSerializable')) {
interface JsonSerializable {
/**
* @return mixed Return data which should be serialized by json_encode().
*/
function jsonSerialize();
}
}
class Card implements JsonSerializable {
/**
* @var string The suit for the card
*/
private $suit;
/**
* @var string The 'number' of the card. A bit of a misnomer, A, J, Q, K can be included.
*/
private $number;
/**
* Creates a new cards of suit $suit with number $number.
* @param string $suit
* @param string $number
* @throws InvalidArgumentException if $suit is not a string.
* @throws InvalidArgumentException if $number is not a string or an int.
*
* @todo More comprehensive checks to make sure each suit as number is valid.
*/
public function __construct($suit, $number) {
if (!is_string($suit)) {
throw new InvalidArgumentException(
'First parameter to Card::__construct() must be a string.'
);
}
if (!is_string($number) && !filter_var($number, FILTER_VALIDATE_INT)) {
throw new InvalidArgumentException(
'Second parameter to Card::__construct() must be a string or an int.'
);
}
$this->suit = $suit;
$this->number = $number;
}
/**
* @return string The suit for the card;
*/
public function suit() {
return $this->suit;
}
/**
* @return string The number for the card;
*/
public function number() {
return $this->number;
}
/**
* Returns a string depicting the card. Although it's json_encoded, don't
* rely on that fact. PHP 5.4 introduces the JsonSerializeable interface,
* which should be used to json_encode an object.
*
* @return string The Card as a string.
*/
public function __toString() {
return json_encode($this->jsonSerialize());
}
/**
* Returns the data that should be encoded into JSON.
* @return array Return data which should be serialized by json_encode().
*/
public function jsonSerialize() {
return get_object_vars($this);
}
}
class Deck implements IteratorAggregate, ArrayAccess, Countable, JsonSerializable {
private $deck;
/**
* Creates a new, unshuffled deck of cards, where the suits are in the order
* of diamonds, hearts, clubs, spades, and each suit is ordered A, 2 .. 10,
* J, Q, K.
*
* @param array $deck [optional] The deck of cards to be used.
* @throws InvalidArgumentException if the any of the elements in $deck are not type Card.
*/
public function __construct(array $deck=null) {
if (isset($deck) && count($deck) > 0) {
foreach ($deck as $card) {
if (!($card instanceof Card)) {
throw new InvalidArgumentException(
'The first parameter to Deck::__construct must be an array'
. ' containing only objects of type Card'
);
}
}
$this->deck = $deck;
} else {
$this->deck = $this->createDeck();
}
}
/**
* Shuffle an array. Uses PHP's shuffle if no function is provided. If a
* function is provided, it must take an array of Cards as its only
* parameter.
* @param callable $function If $function isn't callable, shuffle will be used instead
* @return mixed Returns the result of the shuffle function.
*/
public function shuffle($function = null) {
if (is_callable($function, false, $callable_name)) {
return $callable_name($this->deck);
} else {
return shuffle($this->deck);
}
}
/**
* Used by IteratorAggregate to loop over the object.
* @return ArrayIterator
*/
public function getIterator() {
return new ArrayIterator($this->deck);
}
/**
* @param string $suit The suite to create.
* @return array The cards for the suit.
*/
private function createSuit($suit) {
return array(
new Card($suit, 'A'),
new Card($suit, '2'),
new Card($suit, '3'),
new Card($suit, '4'),
new Card($suit, '5'),
new Card($suit, '6'),
new Card($suit, '7'),
new Card($suit, '8'),
new Card($suit, '9'),
new Card($suit, '10'),
new Card($suit, 'J'),
new Card($suit, 'Q'),
new Card($suit, 'K')
);
}
/**
* Returns a new, unshuffled array of cards, where the suits are in the
* order of diamonds, hearts, clubs, spades, and each suit is ordered:
* A, 2 .. 10, J, Q, K.
* @return array An array of type Card.
*/
private function createDeck() {
return array_merge(
$this->createSuit('diamonds'),
$this->createSuit('hearts'),
$this->createSuit('clubs'),
$this->createSuit('spades')
);
}
/**
* Resets the deck to an unshuffled order, and returns the deck.
* @return \Deck
*/
public function reset() {
$this->deck = $this->createDeck();
return $this;
}
/**
* Returns the data that should be encoded into JSON. Note that any objects
* inside must also be jsonSerialized for anything less than PHP 5.4.
*
* @return mixed Return data which should be serialized by json_encode().
*/
public function jsonSerialize() {
$array = $this->deck;
foreach($array as &$card) {
/**
* @var Card $card
*/
$card = $card->jsonSerialize();
}
return $array;
}
/**
* Used by ArrayAccess. Determine whether an offset(index) exists.
* @param int $index The index to test for existence.
* @return boolean Returns true of the offset exists.
*/
public function offsetExists($index) {
return array_key_exists($index, $this->deck);
}
/**
* Used by ArrayAccess. Returns an item from the index provided.
* @param int $index The index to get..
* @return boolean Returns the object at the location.
* @throws OutOfBoundsException if you specify an index that does not exist.
*/
public function offsetGet($index) {
if (!$this->offsetExists($index)) {
throw new OutOfBoundsException(
"The index '$index' does not exist."
);
}
return $this->deck[$index];
}
/**
* Used by ArrayAccess. Sets an index with the value, or adds a value if it
* is null.
* @param int|null $index The index to set, or null to add.
* @param Card $value The card to set/add.
* @return void
* @throws InvalidArgumentException if the value provided is not a Card.
* @throws InvalidArgumentException if the index provided is not an integer.
* @throws OutOfBoundsException if the index provided does not exist.
*/
public function offsetSet($index, $value) {
if (!($value instanceof Card))
throw new InvalidArgumentException('Decks only contain cards.');
if ($index == null) {
$this->deck[] = $value;
return;
}
if (!is_numeric($index) || $index != (int) $index) {
throw new InvalidArgumentException("Index '$index' must be an integer.");
}
if (!$this->offsetExists($index)) {
throw new OutOfBoundsException("Index '$index' does not exist");
}
$this->deck[$index] = $value;
}
/**
* Unsets the index location.
* @param int $index
* @return void
* @throws InvalidArgumentException if the index provided does not exist.
*/
public function offsetUnset($index) {
if (!$this->offsetExists($index)) {
throw new InvalidArgumentException("Index '$index' Does not exist.");
}
array_splice($this->deck, $index, 1);
}
/**
* Returns a string depicting the card. Although it's json_encoded, don't
* rely on that fact. PHP 5.4 introduces the JsonSerializeable interface,
* which should be used to json_encode an object.
*
* @return string The Card as a string.
*/
public function __toString() {
return json_encode($this->jsonSerialize());
}
/**
* Used by interface Count.
* @return int The size of the deck.
*/
function count() {
return count($this->deck);
}
}
header('Content-type:text/plain');
$deck = new Deck();
echo "Original Deck:
";
foreach ($deck as $card) {
echo $card . "
";
}
$deck->shuffle();
echo "After Shuffle:
";
foreach ($deck as $card) {
echo $card . "
";
}
/**
* Shuffles the array using the Fisher-Yates algorithm.
*/
function fisherYatesShuffle(array &$items) {
for ($i = count($items) - 1; $i > 0; $i--) {
$j = @mt_rand(0, $i);
$tmp = $items[$i];
$items[$i] = $items[$j];
$items[$j] = $tmp;
}
}
$deck->shuffle('fisherYatesShuffle');
echo "Reset, then custom shuffle:
";
foreach ($deck as $card) {
echo $card . "
";
}
echo "First card in the deck:";
echo $deck['0'] . "
";
echo "Deck reset. __toString() on \$deck: ".$deck->reset()."
";
First, determine the number of cards:
$numberOfCards = array_sum(array_map('count', $cards));
Then, create and shuffle the array of indexes:
$indexes = range(0, $numberOfCards);
shuffle($indexes);
Finally, draw numbers from indexes and use the associated card:
foreach ($indexes as $idx) {
foreach ($cards as $name=>$color) {
if ($idx < count($color)) {
$card = $color[$idx];
echo 'Draw: ' . $card . ' of ' . $color . "
";
break;
}
$idx -= count($color);
}
}
Here is my example. Draws a random card, then there is a Draw Again button that draws another random card.
//Matt's Deck - Random Card'
<?php
$cards = array("ah", "ac", "ad", "as",
"2h", "2c", "2d", "2s",
"3h", "3c", "3d", "3s",
"4h", "4c", "4d", "4s",
"5h", "5c", "5d", "5s",
"6h", "6c", "6d", "6s",
"7h", "7c", "7d", "7s",
"8h", "8c", "8d", "8s",
"9h", "9c", "9d", "9s",
"th", "tc", "td", "ts",
"jh", "jc", "jd", "js",
"qh", "qc", "qd", "qs",
"kh", "kc", "kd", "ks");
$deck = array_rand($cards);
switch ($deck){
case '0': echo 'Ace of Hearts';
break;
case '1': echo 'Ace of Clubs';
break;
case '2': echo 'Ace of Diamonds';
break;
case '3': echo 'Ace of Spades';
break;
case '4': echo 'Two of Hearts';
break;
case '5': echo 'Two of Clubs';
break;
case '6': echo 'Two of Diamonds';
break;
case '7': echo 'Two of Spades';
break;
case '8': echo 'Three of Hearts';
break;
case '9': echo 'Three of Clubs';
break;
case '10': echo 'Three of Diamonds';
break;
case '11': echo 'Three of Spades';
break;
case '12': echo 'Four of Hearts';
break;
case '13': echo 'Four of Clubs';
break;
case '14': echo 'Four of Diamonds';
break;
case '15': echo 'Four of Spades';
break;
case '16': echo 'Five of Hearts';
break;
case '17': echo 'Five of Clubs';
break;
case '18': echo 'Five of Diamonds';
break;
case '19': echo 'Five of Spades';
break;
case '20': echo 'Six of Hearts';
break;
case '21': echo 'Six of Clubs';
break;
case '22': echo 'Six of Diamonds';
break;
case '23': echo 'Six of Spades';
break;
case '24': echo 'Seven of Hearts';
break;
case '25': echo 'Seven of Clubs';
break;
case '26': echo 'Seven of Diamonds';
break;
case '27': echo 'Seven of Spades';
break;
case '28': echo 'Eight of Hearts';
break;
case '29': echo 'Eight of Clubs';
break;
case '30': echo 'Eight of Diamonds';
break;
case '31': echo 'Eight of Spades';
break;
case '32': echo 'Nine of Hearts';
break;
case '33': echo 'Nine of Clubs';
break;
case '34': echo 'Nine of Diamonds';
break;
case '35': echo 'Nine of Spades';
break;
case '36': echo 'Ten of Hearts';
break;
case '37': echo 'Ten of Clubs';
break;
case '38': echo 'Ten of Diamonds';
break;
case '39': echo 'Ten of Spades';
break;
case '40': echo 'Jack of Hearts';
break;
case '41': echo 'Jack of Clubs';
break;
case '42': echo 'Jack of Diamonds';
break;
case '43': echo 'Jack of Spades';
break;
case '44': echo 'Queen of Hearts';
break;
case '45': echo 'Queen of Clubs';
break;
case '46': echo 'Queen of Diamonds';
break;
case '47': echo 'Queen of Spades';
break;
case '48': echo 'King of Hearts';
break;
case '49': echo 'King of Clubs';
break;
case '50': echo 'King of Diamonds';
break;
case '51': echo 'King of Spades';
}
?>
<form><input type=button value="Draw Again" onClick="window.location.reload()"></form>
// End Matt's Deck - Random Card
You could use multidimensional associative arrays and fill the second layer with the 52 cards and then shuffle the deck array like this. I only did it this way so I could use the values to compare two blackjack hands as numbers and show who has the higher cards.
$deck = array(
$a = array(
'face' => 'Ace',
'value' => 11,
'suit' => 'Spades'
),
$b = array(
'face' => 'King',
'value' => 10,
'suit' => 'Spades'
),
$c = array(
'face' => 'Queen',
'value' => 10,
'suit' => 'Spades'
),
$d = array(
'face' => 'Jack',
'value' => 10,
'suit' => 'Spades'
),
$e = array(
'face' => 'Ten',
'value' => 10,
'suit' => 'Spades'
),
$f = array(
'face' => 'Nine',
'value' => 9,
'suit' => 'Spades'
),
$g = array(
'face' => 'Eight',
'value' => 8,
'suit' => 'Spades'
),
$h = array(
'face' => 'Seven',
'value' => 7,
'suit' => 'Spades'
),
$i = array(
'face' => 'Six',
'value' => 6,
'suit' => 'Spades'
),
$j = array(
'face' => 'Five',
'value' => 5,
'suit' => 'Spades'
),
$k = array(
'face' => 'Four',
'value' => 4,
'suit' => 'Spades'
),
$l = array(
'face' => 'Three',
'value' => 3,
'suit' => 'Spades'
),
$m = array(
'face' => 'Two',
'value' => 2,
'suit' => 'Spades'
),
$n = array(
'face' => 'Ace',
'value' => 11,
'suit' => 'Clubs'
),
$o = array(
'face' => 'King',
'value' => 10,
'suit' => 'Clubs'
),
$p = array(
'face' => 'Queen',
'value' => 10,
'suit' => 'Clubs'
),
$q = array(
'face' => 'Jack',
'value' => 10,
'suit' => 'Clubs'
),
$r = array(
'face' => 'Ten',
'value' => 10,
'suit' => 'Clubs'
),
$s = array(
'face' => 'Nine',
'value' => 9,
'suit' => 'Clubs'
),
$t = array(
'face' => 'Eight',
'value' => 8,
'suit' => 'Clubs'
),
$u = array(
'face' => 'Seven',
'value' => 7,
'suit' => 'Clubs'
),
$v = array(
'face' => 'Six',
'value' => 6,
'suit' => 'Clubs'
),
$w = array(
'face' => 'Five',
'value' => 5,
'suit' => 'Clubs'
),
$x = array(
'face' => 'Four',
'value' => 4,
'suit' => 'Clubs'
),
$y = array(
'face' => 'Three',
'value' => 3,
'suit' => 'Clubs'
),
$z = array(
'face' => 'Two',
'value' => 2,
'suit' => 'Clubs'
),
$aa = array(
'face' => 'Ace',
'value' => 11,
'suit' => 'Diamonds'
),
$ab = array(
'face' => 'King',
'value' => 10,
'suit' => 'Diamonds'
),
$ac = array(
'face' => 'Queen',
'value' => 10,
'suit' => 'Diamonds'
),
$ad = array(
'face' => 'Jack',
'value' => 10,
'suit' => 'Diamonds'
),
$ae = array(
'face' => 'Ten',
'value' => 10,
'suit' => 'Diamonds'
),
$af = array(
'face' => 'Nine',
'value' => 9,
'suit' => 'Diamonds'
),
$ag = array(
'face' => 'Eight',
'value' => 8,
'suit' => 'Diamonds'
),
$ah = array(
'face' => 'Seven',
'value' => 7,
'suit' => 'Diamonds'
),
$ai = array(
'face' => 'Six',
'value' => 6,
'suit' => 'Diamonds'
),
$aj = array(
'face' => 'Five',
'value' => 5,
'suit' => 'Diamonds'
),
$ak = array(
'face' => 'Four',
'value' => 4,
'suit' => 'Diamonds'
),
$al = array(
'face' => 'Three',
'value' => 3,
'suit' => 'Diamonds'
),
$am = array(
'face' => 'Two',
'value' => 2,
'suit' => 'Diamonds'
),
$an = array(
'face' => 'Ace',
'value' => 11,
'suit' => 'Hearts'
),
$ao = array(
'face' => 'King',
'value' => 10,
'suit' => 'Hearts'
),
$ap = array(
'face' => 'Queen',
'value' => 10,
'suit' => 'Hearts'
),
$aq = array(
'face' => 'Jack',
'value' => 10,
'suit' => 'Hearts'
),
$ar = array(
'face' => 'Ten',
'value' => 10,
'suit' => 'Hearts'
),
$as = array(
'face' => 'Nine',
'value' => 9,
'suit' => 'Hearts'
),
$at = array(
'face' => 'Eight',
'value' => 8,
'suit' => 'Hearts'
),
$au = array(
'face' => 'Seven',
'value' => 7,
'suit' => 'Hearts'
),
$av = array(
'face' => 'Six',
'value' => 6,
'suit' => 'Hearts'
),
$aw = array(
'face' => 'Five',
'value' => 5,
'suit' => 'Hearts'
),
$ax = array(
'face' => 'Four',
'value' => 4,
'suit' => 'Hearts'
),
$ay = array(
'face' => 'Three',
'value' => 3,
'suit' => 'Hearts'
),
$az = array(
'face' => 'Two',
'value' => 2,
'suit' => 'Hearts'
),
);
shuffle ($deck);
echo $deck[0]['face'] . ' of ' . $deck[0]['suit']
</div>
<?php
$a = rand(0 ,3);
suits = array('diamonds', 'hearts', 'clubs', 'spades');
$suit = $suits[$a];
$b = rand(0 ,12);
$v = array('Ace', '2', '3', '4', '5', '6', '7', '8', '9','10','Jack', 'Queen', 'King');
$value = $v[$b];
echo "You have selected the " . $value . " of " . $suit;
?>