Problem in Housie/Bingo game ticket generation - php

I'm trying to create a bingo game that generates 120 tickets. Each Ticket is a [3x9 matrix] and contains numbers between 1-90. Rules for generating tickets are:
RULE #1 - Each row cannot have more than 5 numbers.
RULE #2 - Each column is assigned a range of numbers: (ex. 1-9 can appear only in column 1, 10-19 can appear only in column 2 and so on. And in column 9 numbers between 80-90 can appear)
RULE #3 - In a specific column, numbers must be arranged in ascending order from top to bottom.
RULE #4 - Each column must have at least one number in it.
RULE #5 - All the numbers 1 to 90 are used only once in each set of 6 tickets.
I'm able to fulfill all rules except RULE #5. Here's my code that I've written so far.
$batch_numbers = array(); //to keep track of numbers used in the set of 6 ticekts
$reset = false; // to check if the set is complete
$ticket_set = 1; // set count
for ($t=1; $t <= 120; $t++) {
$ticket_array = array(array(0,0,0,0,0,0,0,0,0), array(0,0,0,0,0,0,0,0,0), array(0,0,0,0,0,0,0,0,0));
$numbers = range(1, 90);
$indices = array();
$random_indices = array();
$used_numbers = array(); // to check unique numbers in a ticket
$full_ticket = array();
for($i=0;$i<3;$i++){
for($j=0;$j<9;$j++){
$indice = array($i, $j);
array_push($indices, $indice);
}
}
#if reset is true (means a set of 6 is complete) so set the batch_numbers array to empty and increment the ticket_set value
if($reset){
$batch_numbers = array();
$reset = false;
$ticket_set++;
}
#if ticket number is divisible by 6 set the reset variable to true to indicate the end of a set of 6 tickets
if(($t%6)==0){
$reset = true;
}
#selecting 5 random positions to fill the first row
$first_row_numbers = range(0,8);
shuffle($first_row_numbers);
$keys_one = array_rand($first_row_numbers, 5);
$first_row = array();
foreach ($keys_one as $row_one_key) {
array_push($first_row, $indices[$row_one_key]);
}
#selecting 5 random positions to fill the second row
$second_row_numbers = range(9,17);
shuffle($second_row_numbers);
$keys_two = array_rand($second_row_numbers, 5);
$second_row = array();
foreach ($keys_two as $row_two_key) {
array_push($second_row, $indices[$second_row_numbers[$row_two_key]]);
}
#selecting 5 random positions to fill the third row
$third_row_numbers = range(18,26);
shuffle($third_row_numbers);
$keys_three = array_rand($third_row_numbers, 5);
$third_row = array();
foreach ($keys_three as $row_three_key) {
array_push($third_row, $indices[$third_row_numbers[$row_three_key]]);
}
#push the selected 5 positions of first row to random_indices array that satisfies RULE #1
foreach($first_row as $row_one){
array_push($random_indices, $row_one);
}
#push the selected 5 positions of second row to random_indices array that satisfies RULE #1
foreach($second_row as $row_two){
array_push($random_indices, $row_two);
}
#push the selected 5 positions of third row to random_indices array that satisfies RULE #1
foreach($third_row as $row_three){
array_push($random_indices, $row_three);
}
#assign the numbers between 1-90 according to its column position to satisfy RULE #2
foreach($random_indices as $indice){
list($row, $column) = $indice;
#generate a unique number for the current set of 6 tickets
$rand = $this->generateRandomColumnNumber($column, $batch_numbers);
#assign the number to the array
$ticket_array[$row][$column] = $rand;
#push the unique number to batch_numbers array so that we can keep track of the numbers used in set of 6 tickets that satisfies RULE #5
array_push($batch_numbers, $rand);
#Push the unique number to used_numbers array so that we can keep track of the numbers already used in the individual ticket
array_push($used_numbers, $rand);
}
#Sort the ticket_array column wise to satisfy the RULE #3
for($k=0; $k<9; $k++){
# if all the rows are filled with random number
if($ticket_array[0][$k] != 0 && $ticket_array[1][$k] != 0 && $ticket_array[2][$k] != 0){
$temp_1 = NULL;
$temp_2 = NULL;
if($ticket_array[0][$k] > $ticket_array[1][$k]){
$temp_1 = $ticket_array[0][$k];
$ticket_array[0][$k] = $ticket_array[1][$k];
$ticket_array[1][$k] = $temp_1;
}
if($ticket_array[1][$k] > $ticket_array[2][$k]){
$temp_2 = $ticket_array[1][$k];
$ticket_array[1][$k] = $ticket_array[2][$k];
$ticket_array[2][$k] = $temp_2;
}
}
# if 1st and 2nd row are filled by random number
elseif($ticket_array[0][$k] != 0 && $ticket_array[1][$k] != 0 && $ticket_array[2][$k] == 0){
if($ticket_array[0][$k] > $ticket_array[1][$k]){
$temp = $ticket_array[0][$k];
$ticket_array[0][$k] = $ticket_array[1][$k];
$ticket_array[1][$k] = $temp;
}
}
# if 1st and 3rd row are filled by random number
elseif($ticket_array[0][$k] != 0 && $ticket_array[1][$k] == 0 && $ticket_array[2][$k] != 0){
if($ticket_array[0][$k] > $ticket_array[2][$k]){
$temp = $ticket_array[0][$k];
$ticket_array[0][$k] = $ticket_array[2][$k];
$ticket_array[2][$k] = $temp;
}
}
# if 2nd and 3rd rows are filled with random numbers
elseif($ticket_array[0][$k] == 0 && $ticket_array[1][$k] != 0 && $ticket_array[2][$k] != 0){
if($ticket_array[1][$k] > $ticket_array[2][$k]){
$temp = $ticket_array[1][$k];
$ticket_array[1][$k] = $ticket_array[2][$k];
$ticket_array[2][$k] = $temp;
}
}
# if 1st, 2nd and 3rd rows are empty we need to assign a value to the column so that it satisfies RULE #4
elseif($ticket_array[0][$k] == 0 && $ticket_array[1][$k] == 0 && $ticket_array[2][$k] == 0){
$modified_array = $this->fillEmptyColumn($k, $ticket_array, $batch_numbers, $batch, $game_id);
$ticket_array = $modified_array;
}
}
/* Code to store the ticket data in database */
}
protected function fillEmptyColumn($column, $ticket_array, $batch_numbers){
for ($i=0; $i < 9; $i++) {
if($i == $column){
continue;
}else{
//Check if all three rows have digits
if($ticket_array[0][$i] != 0 && $ticket_array[1][$i] != 0 && $ticket_array[2][$i] != 0){
$new_number = $this->generateRandomColumnNumber($column, $batch_numbers);
$ticket_array[2][$i] = 0;
$ticket_array[2][$column] = $new_number;
break;
}
//Check if 1st and 2nd rows have digits
elseif($ticket_array[0][$i] != 0 && $ticket_array[1][$i] != 0 && $ticket_array[2][$i] == 0){
$new_number = $this->generateRandomColumnNumber($column, $batch_numbers);
$ticket_array[1][$i] = 0;
$ticket_array[1][$column] = $new_number;
break;
}
//Check if 1st and 3rd rows have digits
elseif($ticket_array[0][$i] != 0 && $ticket_array[1][$i] == 0 && $ticket_array[2][$i] != 0){
$new_number = $this->generateRandomColumnNumber($column, $batch_numbers);
$ticket_array[2][$i] = 0;
$ticket_array[2][$column] = $new_number;
break;
}
//Check if 2nd and 3rd rows have digits
elseif($ticket_array[0][$i] == 0 && $ticket_array[1][$i] != 0 && $ticket_array[2][$i] != 0){
$new_number = $this->generateRandomColumnNumber($column, $batch_numbers);
$ticket_array[2][$i] = 0;
$ticket_array[2][$column] = $new_number;
break;
}
}
}
return $ticket_array;
}
protected function generateRandomColumnNumber($column, $batch_numbers)
{
#assign the numbers according to the column
if($column == 0){
$rand = rand(1, 9);
}elseif($column == 8){
$rand = rand(80,90);
}
else{
$rand = rand(($column *10), ((($column+1)*10)-1));
}
# check if numbers already exists in the current set of 6 tickets
if(in_array($rand, $batch_numbers)){
return $this->generateRandomColumnNumber($column, $batch_numbers);
}
return $rand;
}
Every time I try to call the generateRandomColumnNumber to generate a unique number and pass the batch_numbers array (to check unique number in the set of 6 tickets) instead of used_numbers array ( to check unique number in a individual ticket ) I get 500 error instantly but doesn't show why it is caused. Could anyone please help me out to point out what's wrong in my code and help me solve this. Been stuck at this for a couple of days now. It'd be of great help. Thanks

Related

Remove values from array depending on conditions

This is more of an algorithm problem than a PHP problem, but I can't seem to figure it out!
I've got a multi dimensional array as following :
[
210875 => ["2", "1"],
129096 => ["2", "2"]
]
There are 2 ids but there could be more. I want to keep in my table only 1 id depending on these conditions.
Pseudo code:
if a[0] == 1 and a[1] >= 1: # the id gets deleted right away without looking at other conditions
if a[0] >= 2 and a[1] == 2: # it stays if nothing below fits else it gets deleted
if a[0] >= 3 and a[1] == 3: # it stays if nothing below fits else it gets deleted
if a[0] >= 8 and a[1] == 4: # it stays if nothing below fits else it gets deleted
# ... etc
If two fit the same conditions then one of them randomly gets deleted.
For example (pseudo code):
a[0] == 2 and a[1] == 1
a[0] == 2 and a[1] == 2
I've tried doing the following, but I can't figure out how to dynamically have if-conditions made dependent on my algorithm :
$sub_array = array();
foreach($array as $k=>$v) {
if($v[0] == 1 && $v[1] == 1) continue;
if($v[0] >= 2 && $v[1] == 2) $sub_array[$k] = $v;
if($v[0] >= 6 && $v[1] == 3) $sub_array[$k] = $v;
if($v[0] >= 8 && $v[1] == 4) $sub_array[$k] = $v;
// etc ...
}
Also, when I do that, how to know which one is the highest in my sub array?
It is not entirely clear to me what your conditions are, but to me it looks like you really are giving a rank to each of your rows, where in the end you want to keep the row with the highest rank.
Even if my interpretation of your rules might not be exactly what you intended, you can tune it by adapting the scores in the code below. I would also suggest to swap the conditions so that the == comparison comes first. That will be a bit more efficient:
$keepKey = null;
$bestScore = -1;
foreach($array as $k=>$v) {
$score = 0;
if ($v[1] == 1 && $v[0] == 1) $score = 1;
if ($v[1] == 2 && $v[0] >= 2) $score = 2;
if ($v[1] == 3 && $v[0] >= 6) $score = 3;
if ($v[1] == 4 && $v[0] >= 8) $score = 4;
// ... etc ...
if ($score > $bestScore) {
$keepKey = $k;
$bestScore = $score;
}
}
$sub_array = [$keepKey => $array[$keepKey]]; // Only keep the entry with highest score
If it is important to really pick a random one in case of ties, then shuffle the keys before starting the loop:
$keepKey = null;
$bestScore = -1;
$keys = array_keys($array); // <---
shuffle($keys); // <
foreach($keys as $k) { // <
$v = $array[$k]; // <---
$score = 0;
if ($v[1] == 1 && $v[0] == 1) $score = 1;
if ($v[1] == 2 && $v[0] >= 2) $score = 2;
if ($v[1] == 3 && $v[0] >= 6) $score = 3;
if ($v[1] == 4 && $v[0] >= 8) $score = 4;
echo $score . "\n";
if ($score > $bestScore) {
$keepKey = $k;
$bestScore = $score;
}
}
$sub_array = [$keepKey => $array[$keepKey]];

Combine 2 while loop results

We have 2 while loops , we are displaying 2 different results with both.
we need to combine or without combining we need to merge 2 results.
in below image ,
1st while loop result = > 1st, 2nd, 6th rows.
2nd while loop result = > 3rd, 4th, 5th rows.
we need 3rd , 4th & 5th rows results in 1st, 2nd 6th rows in last 2 columns [ Paid status & commission ].
1st while results coming from Database 1 & 2nd while results are coming from Database 2 with table [order_details]
$stmt = $user_home->runQuery("SELECT * FROM order_details");
$stmt->execute(array(":uid" => $_SESSION['userSession']));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->execute();
$i = 0;
foreach($order as $orderData)
{
$k = 0;
$orderitems = $orderData['dproduct_id'];
$orderitemsarray = explode(",", $orderitems);
/* 1st while */
while ($k < count($orderitemsarray))
{
if ($orderitemsarray[$k] != '0')
{
$stmtorders = $user_home->runQuery("SELECT * FROM order_details");
$stmtorders->execute(array(":dorder_id" => $orderData['entity_id']));
$roworders = $stmtorders->fetch(PDO::FETCH_ASSOC);
$dorderStatus = $roworders['dpaid_status'];
$productdetail = Mage::getModel('catalog/product')->load($orderitemsarray[$k]);
$designer_id = $productdetail->getDesignerID() ;
if($accountType == "admin")
{
$designerName = getDesignerName($productdetail->getDesignerID()) . " -(" . $productdetail->getDesignerID() . ")";
$responce[] = array(
$orderData->getIncrementId() ,
$orderitemsarray[$k],
$productdetail->getName() ,
$designerName,
$orderData['status'],
$data['dpaid_status'],
$data['commission'],
$sDate
);
}
}
$k++;
$i++;
}
/* 2nd while */
while($data = $stmt->fetch())
{
$responce[] = array(
$data['dorder_id'],
$data['dpaid_status'],
$data['commission']
);
$k++;
}
}
I tried below code , but it results as below image - means only 2 rows displayed instead of 23 rows....
while (($k < count($orderitemsarray)) && ($data = $stmt->fetch()))
Full page looks as below :
I am new to php world & tried lot before posting here....
The thing is whenever you use empty brackets [] an index is automatically assigned which is equal to the next available numeric value and your data ends up in a next position so correct index is equired to solve this issue .
In while (($k < count($orderitemsarray)) && ($data = $stmt->fetch()))
If either of condition fails then loop ends and your $data probably has only three entries it's showing only that many even though $orderitemsarray has many more , i am not sure but you can prolly replace while with if statement and change index like this to make those stuff append on same row
$indx=0;
foreach($order as $orderData)
{
$k = 0;
//.. Stufff
/* 2nd while */
if($indx == 0 || $indx == 1 || $indx == 5)
{
if($data = $stmt->fetch())
{
$size = count($responce); // get size from that get its last index
$responce[$size-1] = array(
$data['dorder_id'],
$data['dpaid_status'],
$data['commission']
);
$k++; //<-- not sure why it's here but doesn't matter may be some magento stuff?
}
}
$indx++
}
EDIT:sry my bad , ignore my last comment try this instead
$responce[$size-1] = array_merge($responce[$size-1] , array(
$data['dorder_id'],
$data['dpaid_status'],
$data['commission']
) );

Check for poker straight

I have managed to create an algorithm to check the rank of a poker hand. It works 100% correctly, but it's very slow. I've been analysing the code, and the check straight function is one of the slowest parts of it.
So my question is, is there a better way of calculating whether a hand make a straight?
Here is some details:
7 cards, 2 from holder, 5 from board. A can be high or low.
Each card is assigned a value:
2 = 2
3 = 3
..
9 = 9
T = 10
J = 11
Q = 12
K = 13
A = 14
The script has an array of all 7 cards:
$cards = array(12,5,6,7,4,11,3);
So now I need to be able to sort this into an array where it:
discards duplicates
orders the card from lowest to highest
only returns 5 consecutive cards I.e. (3,4,5,6,7)
It needs to be fast; loops and iterations are very costly. This is what I currently use and when it tries to analyse say 15000 hands, it takes its toll on the script.
For the above, I used:
discard duplicates (use array_unique)
order cards from lowest to highest (use sort())
only return 5 consecutive cards (use a for loop to check the values of cards)
Does anyone have any examples of how I could improve on this? Maybe even in another language that I could perhaps look at and see how it's done?
Instead of working with array deduping and sorting, consider using a bitmask instead, and setting bits to 1 where the card value is set. A bitmask works like a Set datastructure and comes with additional advantages when it comes to detecting contiguous elements.
for ($i = 0; $i < count($cards); $i++) {
$card = $cards[$i];
// For each card value, set the bit
if ($card == 14) {
// If card is an ace, also set bit 1 for wheel
$cardBitmask |= 0x2;
}
$cardBitmask |= (1 << $card);
}
// To compare, you simply write a for loop checking for 5 consecutive bits
for($i = 10; $i > 0; $i--)
{
if ($cardBitmask & (0x1F << $i) == (0x1F << $i)) {
// Straight $i high was found!
}
}
Consider the Java implementation at this link. I've included it here:
public static boolean isStraight( Card[] h )
{
int i, testRank;
if ( h.length != 5 )
return(false);
sortByRank(h); // Sort the poker hand by the rank of each card
/* ===========================
Check if hand has an Ace
=========================== */
if ( h[4].rank() == 14 )
{
/* =================================
Check straight using an Ace
================================= */
boolean a = h[0].rank() == 2 && h[1].rank() == 3 &&
h[2].rank() == 4 && h[3].rank() == 5 ;
boolean b = h[0].rank() == 10 && h[1].rank() == 11 &&
h[2].rank() == 12 && h[3].rank() == 13 ;
return ( a || b );
}
else
{
/* ===========================================
General case: check for increasing values
=========================================== */
testRank = h[0].rank() + 1;
for ( i = 1; i < 5; i++ )
{
if ( h[i].rank() != testRank )
return(false); // Straight failed...
testRank++; // Next card in hand
}
return(true); // Straight found !
}
}
A quick Google search for "check for poker straight (desired_lang)" will give you other implementations.
You could just sort the cards and loop over them in an array - saving always the last card and compare them with the current one.
$cards = array(12,5,6,7,4,11,3);
sort($cards);
$last = 0;
$count = 0;
$wheel = false;
foreach ($cards as $card) {
if ($card == $last) {
continue;
} else if ($card == ++$last) {
$count++;
} else {
if ($last == 6) $wheel = true;
$count = 1;
$last = $card;
}
if ($count == 5 || ($card == 14 && $wheel)) {
echo "straight $last";
$straight = range($last - 4, $last);
break;
}
}
You may go like this, you don't need to sort or anything (assuming that 2 is 2 and 14 is ace):
$cards = [12,5,6,7,4,11,3];
function _inc(&$i) {
if ($i == 14)
$i = 2;
else
$i++;
return $i;
}
$straight = false;
for($i = 2; $i <= 14; $i++) {
$ind = $i;
if (!in_array($ind, $cards)) continue;
$s = [$ind, _inc($ind), _inc($ind), _inc($ind), _inc($ind)];
$straight = count(array_intersect($s, $cards)) == count($s);
if ($straight) break;
}
print $straight;

How to tell if a comma delimited list of numbers obeys the natural order of numbers

I have a comma delimited list of numbers which i am converting into an array and what i want to know about the list of numbers is if the numbers listed obey a natural ordering of numbers,you know,have a difference of exactly 1 between the next and the previous.
If its true the list obeys the natural ordering,i want to pick the first number of the list and if not the list obeys not the natural order,i pick the second.
This is my code.
<?php
error_reporting(0);
/**
Analyze numbers
Condition 1
if from number to the next has a difference of 1,then pick the first number in the list
Condition 2
if from one number the next,a difference of greater than 1 was found,then pick next from first
Condition 3
if list contains only one number,pick the number
*/
$number_picked = null;
$a = '5,7,8,9,10';
$b = '2,3,4,5,6,7,8,9,10';
$c = '10';
$data = explode(',', $b);
$count = count($data);
foreach($data as $index => $number)
{
/**
If array has exactly one value
*/
if($count == 1){
echo 'number is:'.$number;
exit();
}
$previous = $data[($count+$index-1) % $count];
$current = $number;
$next = $data[($index+1) % $count];
$diff = ($next - $previous);
if($diff == 1){
$number_picked = array_values($data)[0];
echo $number_picked.'correct';
}
elseif($diff > 1){
$number_picked = array_values($data)[1];
echo $number_picked.'wrong';
}
}
?>
The problem i am having is to figure out how to test the difference for all array elements.
No loops are needed, a little bit of maths will help you here. Once you have your numbers in an array:
$a = explode(',', '5,7,8,9,10');
pass them to this function:-
function isSequential(array $sequence, $diff = 1)
{
return $sequence[count($sequence) - 1] === $sequence[0] + ($diff * (count($sequence) - 1));
}
The function will return true if the numbers in the array follow a natural sequence. You should even be able to adjust it for different spacings between numbers, eg 2, 4, 6, 8, etc using the $diff parameter, although I haven't tested that thoroughly.
See it working.
Keep in mind that this will only work if your list of numbers is ordered from smallest to largest.
Try using a function to solve this... Like so:
<?php
error_reporting(0);
/**
Analyze numbers
Condition 1
if from number to the next has a difference of 1,then pick the first number in the list
Condition 2
if from one number the next,a difference of greater than 1 was found,then pick next from first
Condition 3
if list contains only one number,pick the number
*/
$number_picked = null;
$a = '5,7,8,9,10';
$b = '2,3,4,5,6,7,8,9,10';
$c = '10';
function test($string) {
$data = explode(',', $string);
if(count($data) === 1){
return 'number is:'.$number;
}
foreach($data as $index => $number)
{
$previous = $data[($count+$index-1) % $count];
$current = $number;
$next = $data[($index+1) % $count];
$diff = ($next - $previous);
if($diff == 1){
$number_picked = array_values($data)[0];
return $number_picked.'correct';
}
elseif($diff > 1){
$number_picked = array_values($data)[1];
return $number_picked.'wrong';
}
}
}
echo test($a);
echo test($b);
echo test($c);
?>
You already know how to explode the list, so I'll skip that.
You already handle a single item, so I'll skip that as well.
What is left, is checking the rest of the array. Basically; there's two possible outcome values: either the first element or the second. So we'll save those two first:
$outcome1 = $list[0];
$outcome2 = $list[1];
Next, we'll loop over the items. We'll remember the last found item, and make sure that the difference between the new and the old is 1. If it is, we continue. If it isn't, we abort and immediately return $outcome2.
If we reach the end of the list without aborting, it's naturally ordered, so we return $outcome1.
$lastNumber = null;
foreach( $items as $number ) {
if($lastNumber === null || $number - $lastNumber == 1 ) {
// continue scanning
$lastNumber = $number;
}
else {
// not ordened
return $outcome2;
}
}
return $outcome1; // scanned everything; was ordened.
(Note: code not tested)
To avoid the headache of accessing the previous or next element, and deciding whether it still is inside the array or not, use the fact that on a natural ordering the item i and the first item have a difference of i.
Also the corner case you call condition 3 is easier to handle outside the loop than inside of it. But easier still, the way we characterize a natural ordered list holds for a 1-item list :
$natural = true;
for($i=1; $i<$count && $natural; $i++)
$natural &= ($data[$i] == $data[0] + $i)
$number = $natural ? $data[0] : $data[1];
For $count == 1 the loop is never entered and thus $natural stays true : you select the first element.

Multiple counters not counting ( foreach / ifs)

This is my first StackOverFlow post.
I have an array of numbers, which I shuffle:
$nums = array("1","1","1","1","1","2","2","2","2","3","3","3","4","4","4");
shuffle($nums);
I am trying to identify a sequence/pattern of any three 3 identical numbers in a row in the shuffled array and
output the combined total of all sets of matching numbers.
The problem that I am running into seems to stem from attempting to compare the current number in the loop with the previous number (to see if they match).
When I echo the "previous" number it always outputs as "0". Thus I am unable to compare the current number and old number, which means I am not able to sum identify and sum a pattern of identical numbers.
Here is my code:
<?php
$t3count = 0;
$oldnum = 0;
$tots = 0;
$nums = array("1","1","1","1","1","2","2","2","2","3","3","3","4","4","4");
shuffle($nums);
foreach ($nums as $num) {
echo "$num: [$oldnum] ";
if ($num = $oldnum) {
$t3count++;
if ($t3count = 3) {
$tots = $num * $num;
$t3count = 0;
$oldnum = $num;
} else {
# do nonum
}
}
else {
$oldnum = $num;
}
# echo "<li>$num</li>";
}
echo "Your total is: $tots";
unset($num);
?>
Thank you.
You need to do comparison == not assignment = here:
if ($num = $oldnum)
and here:
if ($t3count = 3)
also this is probably going to bite you if i got the logic right
$t3count++;
if ($t3count == 3) {
how do you know which one counted to 3, id build nested arrays of like values first then process that.
you missed == to compare if ($num = $oldnum) and if ($t3count = 3)
replace with
if ($num == $oldnum)
if ($t3count == 3)

Categories