phparraysassociative-arraypokersequential-number

How do I extract the highest value from a set of sequential numbers within an array?


I'm trying to calculate whether or not a user has a straight in a poker hand using values from an associative array.

Each player's hand is inside the same array, the structure of which being similar to this:

<?php
// Note: the first 5 values of each player's hand are identical because those are
// the dealer's cards (the cards on the "board" that everyone can see).
// The last two values represent the player's "hole cards" that only they can see.

// Also note: 11 = Jack, 12 = Queen, 13 = King, 14 = Ace.
$hands = array(
  // Player 0
  0 => array(
    0 => array("value_num" => 6),
    1 => array("value_num" => 7),
    2 => array("value_num" => 8),
    3 => array("value_num" => 9),
    4 => array("value_num" => 14),
    5 => array("value_num" => 10),
    6 => array("value_num" => 8),
  ),
  // Player 1
  1 => array(
    0 => array("value_num" => 6),
    1 => array("value_num" => 7),
    2 => array("value_num" => 8),
    3 => array("value_num" => 9),
    4 => array("value_num" => 14),
    5 => array("value_num" => 10),
    6 => array("value_num" => 13),
  ),
  // Player 2
  2 => array(
    0 => array("value_num" => 6),
    1 => array("value_num" => 7),
    2 => array("value_num" => 8),
    3 => array("value_num" => 9),
    4 => array("value_num" => 14),
    5 => array("value_num" => 5),
    6 => array("value_num" => 12),
  ),
  // Player 3
  3 => array(
    0 => array("value_num" => 6),
    1 => array("value_num" => 7),
    2 => array("value_num" => 8),
    3 => array("value_num" => 9),
    4 => array("value_num" => 14),
    5 => array("value_num" => 5),
    6 => array("value_num" => 13),
  ),
);

The function I've written to determine if the player has a straight looks like this:

<?php
/**
 * @return
 *   The highest card value of the player's straight, if they have a straight.
 *   Otherwise, 0.
 */
function has_straight($cards, $player_id, $required_consecutive_cards = 5) {
  // Extract the "value_num" values to calculate the straight.
  for ($i = 0; $i < count($cards[$player_id]); $i++) {
    $values[$player_id][$i] = $cards[$player_id][$i]['value_num'];
  }

  // Check to see if the player has an Ace within their hand.
  $has_ace[$player_id] = array_search(14, $values[$player_id]);

  // If the player has an Ace, push a 1 into their $value array in case they also
  // have 2, 3, 4, and 5 in their hand.
  if ($has_ace[$player_id] !== FALSE) {
    array_push($values[$player_id], 1);
  }

  // Only check the player's unique values. Right now we're not concerned with
  // pairs or anything else.
  $player_cards[$player_id] = array_unique($values[$player_id]);

  // Reverse sort the player's unique values to make counting sequences easier.
  rsort($player_cards[$player_id]);

  // Set a number to increment if one card is sequential to the other.
  $increment_value[$player_id] = 1;
  foreach ($player_cards[$player_id] as $key => $value) {
    $next_key = $key + 1;
    $next_value = $value - 1;
    if (!array_key_exists($next_key, $player_cards[$player_id])) {
      return 0;
    }
    if ($player_cards[$player_id][$next_key] == $next_value) {
      $increment_value[$player_id]++;
    }
    else {
      $increment_value[$player_id] = 1;
      unset($player_cards[$player_id][$key]);
    }
    if ($increment_value[$player_id] == $required_consecutive_cards) {
      // Reverse sort what's left of the values so that the first one can be
      // the value returned.
      rsort($player_cards[$player_id]);
      return $player_cards[$player_id][0];
    }
  }
  return 0;
}

So, to determine if player 0 has a straight, you could run this:

<?php
$player_to_check = 0;
print has_straight($hands, $player_to_check);

... which should result in 10 as the output (10 being the highest value within player 0's straight -- 6, 7, 8, 9, 10).

The problem I'm having is when, for example, you check player 1's straight -- which should be identical to player 0's straight -- the output will be 14 instead of 10.

The reason this is happening is because player 1 also has a 13 in his hand, and since 13 is sequential with 14, 14 is returned instead of 10 (even though 14 is not within player 1's straight).

The same problem can be seen when you compare players 3 and 4.

What do I need to do inside the function to make it so the highest value within the set of 5 sequential numbers is returned (as opposed to the highest value of the first set of sequential numbers found)?


Solution

  • A straight has 5 consecutive cards, with a sum always obeying this rule: (n*5)+10. Where n is the starting card. The higher the sum, the higher the starting card. So this could be a solutiuon:

    foreach($player_id as $id){
        //GET THE CARD VALUES FROM THIS PLAYER
        $cards=array();
        foreach($player_cards[$id] as $tmp){
            $cards[] = $tmp['value_num'];
            }
        //ONLY THE UNIQUE CARDS
        $cards=array_unique($cards);
        //SORT THEM LOW TO HIGH
        sort($cards);
    
    
        //Get the maximum amount of possabilities for a straight from 
          //the maximum 7 cards in a hand
        $max=count($cards)-4;
        //IF TOO LITTLE CARDS, CONTINUE WITH NEXT PLAYER
        if($max<=0)continue;
    
        //SET SUM and HIGHEST SUM
        $sum=0;
        $hsum=0;
    
        //LOOP THE POSSIBLE STRAIGHTS
        for($i=1;$i<$max+1;++$i){
            //GET 5 CONSECUTIVE CARDS
            $straight=array_slice($cards,$i-1,5);
    
            $sum_should_be=($straight[0]*5)+10;
            $sum = array_sum($straight);
    
            //IF SUM NOT CORRECT, CONTINUE WITH NEXT POSSIBLE STRAIGHT
            if($sum!=$sum_should_be)continue;
            //GET THE HIGHEST STRAIT FOR USER
            if($sum>$hsum)$hsum=$sum;
        //ADD STRAIGHT SCORE TO ARRAY FOR COMPARISON
        $straight_score[$id]=$straight[4];
        }
    }
    

    It will give you:

    Player 0. Straight score: 10
    Player 1. Straight score: 10
    Player 2. Straight score: 9
    Player 3. Straight score: 9