PHP actions in ratio - php

I have 3 options, and they have to happen in a particular ratio.
Out of 10 times, this is the ratio:
8x Option 1
1x Option 2
1x Option 3
Right now I have the following:
if (rand(8,10)) {
option1();
} elseif (rand(1,10)) {
option2();
} elseif (rand(1,10)) {
option3();
}
But this gets option 1 way more than 8 times. Option 2 never occurs and option 3 rarely.
So, what is the correct way to achieve this?

Try with something like this.
$roll = rand(1,10);
if ($roll == 1) {
option2();
} elseif ($roll == 2) {
option3();
} else {
option1();
}
If random number is 1 (10% of the time), option 2.
If random number is 2 (10% of the time), option 3.
If random number is anything else (80% of the time), option 1.

$r = rand() / getrandmax();
if ($r <= 0.8) {
option1();
} elseif ($r <= 0.9) {
option2();
} else {
option3();
}

No, this is not the right way. You can use something like:
<?php
// Store in DB use of options 1, 2 and 3
// field value
// -----------------
// options1 0
// options2 0
// options3 0
// If $options1 = 8, $options2= 1 and $options3 = 1, then clear all values from DB
$random = rand(1,3);
if ($random == 1) {
if ($options1 < 9) { option1(); } else { // tryagain; }
} elseif (
...
// update DB with new value for used option
Sorry the code is slightly messed up (the if part) but I guess you can understand. Let me know if you need the whole code.

Related

PHP random/percentage chance to do one of the options

I'm looking to have a link that if a user presses it generates a random number, that number is then compared to a bunch of if statements to do something e.g something like this:
$value = rand(1,1000);
Then if statements:
if ($value >= 1 && $value <= 10) {
// do something
} elseif ($value >= 11 && $value <= 20) {
// do something else
} .... etc
Each random click/number will only relate to one of the if statements, so only one output should happen.
This doesn't seem the most logical/simplest way to do this. Unsure what the best method would be here. Could anyone point me in the right direction of other methods to achieve something like this? Thanks!
You could use a switch statement like this:
$numberOfSomethings = 5;
switch (rand(1,$numberOfSomethings)) {
case 1:
// do something number 1
break;
case 2:
// do something number 2
break;
case 3:
// do something number 3
break;
case 4:
// do something number 4
break;
case 5:
// do something number 5
break;
}
but this looks very much like your code.
You could do the "something" inside functions and then call a random function:
function something1()
{
// do something number 1
}
function something2()
{
// do something number 3
}
function something3()
{
// do something number 3
}
$functionName = "something".rand(1,3); // choose a function
$functionName(); // and call it
This seems easy to expand. But I still think your goals are not very clear.
So what you are after is thresholds with callbacks. So what you need to build is an array of thresholds that have a callback. Then when you generate your random number you just iterate through the thresholds and compare your random number to the threshold value.
$number = rand(1, 1000);
$thresholds = [
[10,function(){}],
[20,function(){}],
[30,function(){}]
];
//now we can loop through each threshold with the random number. As we only need to check the last highest value of each to know we passed the threshold.
foreach($thresholds as $index=> $threshold){
//we need to check if this is the last threshold first so we need to know what that is.
if(array_key_last($threshold) === $index){
$threshold[1]();
break;
}
//here we check if the number is greater than the threshold and also less than the next threshold. then call its callback.
elseif($number >= $threshold[0] && $number < $thresholds[$index + 1][0]){
$threshold[1]();
break;
}
You liked the function idea, but you need varying odds. That can be done with a lookup array. So we still have these functions:
function something1()
{
// do something number 1
}
function something2()
{
// do something number 3
}
function something3()
{
// do something number 3
}
but now we use a lookup array like this:
$events = ['something1' => 3,
'something2' => 1,
'something3' => 5];
The total sum of that array is 9, and something2 has a 1 in 9 chance of occurring, something1 a 1 in 3 chance and something3 a 5 in 9 chance.
Now we still need to call a function using these chances, that can be done this way:
$total = array_sum($events);
$number = rand(1, $total);
$sum = 0;
foreach ($events as $functionName => $chance) {
$sum += $chance;
if ($sum >= $number) {
$functionName();
break;
}
}

how to reduce number of condition checks when I select one/two/three/four/five/six from the 6 select options?

Example: When I have only two select options
input_one = 'one';
input_two = 'two';
when I select only input_one
if((isset($_POST['input_one']) && (!isset($_POST['input_two']) {
//query
}
When I select only input_two
elseif((!isset($_POST['input_one']) && (isset($_POST['input_two'])) {
//query
}
When I don't select any of those
elseif((!isset($_POST['input_one']) && (!isset($_POST['input_two'])) {
//query
}
When I select both of them
elsif((isset($_POST['input_one']) && (!isset($_POST['input_two'])) {
//query
}
but when I have 6 input options then I need to write a number of conditions. How do I reduce those, if there is any alternate method?
$query = "some query";
foreach($_POST as $key => $post){
switch($key){
case 1:
$query .= " AND some condition";
break;
case 2:
$query .= " AND some condition";
break;
}
}
mysqli_query($sql,$query);
I will give my answer based on the hypothesis, that the inputs are dependent on each other. Or let's call it interdependent.
If you are looking at your code, you'll notice that your conditions can be abstracted by basic binary calculations.
So let's assume we speak about two states:
isset = 1,
!isset = 0
And each input can have one of the two states.
If you have 1 input, then you can have 2exp(1) = 2 possibilities.
0
1
If you have 2 inputs, then you can have 2exp(2) = 4 possibilities. Nota bene: each column represents the states that an input can take in dependence with the other one.
00
01
10
11
If you have 3 inputs, then you can have 2exp(3) = 8 possibilities.
000
001
010
011
100
101
110
111
...
If you have 6 inputs, then you can have 2exp(6) = 64 states in total.
000000
000001
...
111110
111111
The general formula is:
N = 2exp(x)
Where:
N = total number of possibilities;
x: Number of observable elements, each can achieve 2 states (0 or 1).
So, as you see, it's hard to maintain such a structure.
And take into consideration that you'll probably need to make validations on empty() values too. At least.
I recommend you, that you really change something in your code, so that your inputs are not anymore so dependable on each other.
So, my answer would be "no", there is no way to do it, better than you already do it. The if-elseif-else is the proper one. But that doesn't help you a lot if you have more than, let's say 3 interdependent inputs.
EDIT 1:
One relative maintainable solution would be to divide the interdependent inputs in groups of maximum 3. And only for these 3 to send a request to the server.
EDIT 2:
Or to ensure that in one request moment, only one input has been set or have a value.
Or to ensure that each of the inputs have at least a default value (like '0') on the request moment.
an example for four conditions:
<?php
$c = 0;
//basic conditions...............
if( isset($_POST['input_1'] ) $c += 1; //set 1st bit
if( isset($_POST['input_2'] ) $c += 2; //set 2nd bit
if( isset($_POST['input_3'] ) $c += 4; //set 3rd bit
if( isset($_POST['input_4'] ) $c += 8; //set 4th bit
//additional conditions...............
if($c < 8){ //0..7
if($c < 4){ //0..3
if($c < 2){ //0..1
if($c == 0){ //nothing set
}else{ // 1 //input_1
}
}else{ //2..3
if($c == 2){ //input_2
}else{ //3 //input_1 input_2
}
}
}else{ //4..7
if($c < 6){//4..5
if($c == 4){ //input_3
}else{ // 5 //input_1 input_3
}
}else{//6..7
if($c == 6) { //input_2 input_3
}else{ // 7 //input_1 input_2 input_3
}
}
}
}else{ //8..15
if($c < 12){ //8..11
if($c < 10){ //8..9
if($c == 8){ //input_4
}else{ // 9 //input_1 input_4
}
}else{ //10..11
if($c == 10){ //input_2 input_4
}else{ //11 //input_1 input_2 input_4
}
}
}else{ //12..15
if($c < 14){//12..13
if($c == 12){ //input_3 input_4
}else{ // 13 //input_1 input_3 input_4
}
}else{//14..15
if($c == 14) { //input_2 input_3 input_4
}else{ // 15 //input_1 input_2 input_3 input_4
}
}
}
}
?>
In this case we have to check 8 condition to get all the issets states.
So it could not be the optimal solution when we have only 4 parameters what produces only 2^4=16 possibilities.
But if we take 6 arguments:
Then we have to check 6 basic onditions and 6 additionals.
So it gives 12 conditions to check when there is 2^6=64 possibilites.
Lets take 7 args:
Then we have 7 + 7 = 14 condition to check to get one of 2^7=128 possibilities.
And now we start to see advantages for bigger amount of arguments.

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 merge two sets of data/array dynamically based on their count

So, I have this requirement that I have two sets of data (stored in arrays, but can be anything). What I want to do is that I want to add these two sets together so that the final result count is 10.
The scenarios are:
Both sets can initially have more than 5 (or 10 for that matter). In which case it is easy - I just take 5 from each set and add them together and display
Either set could be less than 5. In which case, I should take whatever is available in that set. In the other set, I should take how-much-ever is required to make total 10, If the other set's count is low such that taking it does not make total to 10, then I should take it all and display whatever I got.
Based on this requirement, I am trying to write a logic which will give me the count required from each set, however the if-else-if-else is getting too complicated that I think I might be doing it incorrectly. Can anyone help me with creating a simpler logic to do what I need?
My current (incomplete and convoluted) logic is:
if($set1Count >= 5)
{
$requiredSet1Count = 5;
if($set2Count >= 5)
{
$requiredSet2Count = 5;
}
else
{
$requiredSet2Count = $set2Count;
if($requiredSet1Count > = (10 - $set2Count))
{
$requiredSet1Count = (10 - $set2Count);
}
else
{
$requiredSet1Count = $set1Count;
}
}
}
else
{
.....// I gave up by the time I reached here....
}
In above code $set1Count and $set2Count are the actual result counts in the two sets/arrays. The $requiredSet1Countand $requiredSet2Count are the dynamic counts I need which will tell me how many elements to extract from each set.
Any help is greatly appreciated!
I don't know a variant without ifs. Lets try to use one for every situation
function requiredSet($set1count, $set2count) {
// Both arrays together contain less than 10 items
if ($set1count + $set2count <= 10) {
$requiredSet1Count = $set1count; $requiredSet2Count = $set2count;
}
// 1st less than 5 elements
elseif ($set1count < 5) {
$requiredSet1Count = $set1count;
$requiredSet2Count = $set2count + $set1count > 10 ? 10 - $set1count : $set2count;
}
// 2nd - less than 5 elements
elseif ($set2count < 5) {
$requiredSet2Count = $set2count;
$requiredSet1Count = $set1count + $set2count > 10 ? 10 - $set2count : $set1count;
}
// Just take 5 elements in each
else $requiredSet1Count = $requiredSet2Count = 5;
return array($requiredSet1Count, $requiredSet2Count);
}
echo '<pre>';
var_export(requiredSet(1,2)); echo '<br>';
var_export(requiredSet(2,5)); echo '<br>';
var_export(requiredSet(2,7)); echo '<br>';
var_export(requiredSet(2,13)); echo '<br>';
var_export(requiredSet(13,11)); echo '<br>';
result
array ( 0 => 1, 1 => 2)
array ( 0 => 2, 1 => 5)
array ( 0 => 2, 1 => 7)
array ( 0 => 2, 1 => 8)
array ( 0 => 5, 1 => 5)

filter keys from array? php poker

Now I know the basic logic behind finding a straight, and I assume that would include a pseudo of
function is_straight(array $cards) {
sort($cards);
if(($cards[4] - $cards[0]) == 5) {
//Code to make sure the cards in between are increment
//is straight.
}
}
would theoretically work for a 5 card check.
But how would one go for eliminating cards from the array of 7 cards to find a straight?
Would I have to individually check all 5 hand combinations within the 7 cards array?
so eliminate two cards from the $cards array and check that combination for a straight?
So I'm a little stuck on the logical side of this, rather than the code side.
In pseudo code
#filter doubles
cards = array_unique(cards)
sort(cards)
foreach cards as key, value:
if not key_exists(cards, key+4):
return false
if cards[key+4] == value + 4:
return true
longer potentially more explicit version
#filter doubles
cards = array_unique(cards)
sort(cards)
straight_counter = 1
foreach cards as key, value:
if not key_exists(cards, key+1):
return false
# is the following card an increment to the current one
if cards[key+1] == value + 1:
straight_counter++
else:
straight_counter = 1
if straight_counter == 5:
return true
function is_straight(array $array) {
$alpha = array_keys(array_count_values($array));
sort($alpha);
if (count($alpha) > 4) {
if (($alpha[4] - $alpha[0]) == 4) {
$result = $alpha[4];
return $result;
}
if (count($alpha) > 5) {
if (($alpha[5] - $alpha[1]) == 4) {
$result = $alpha[5];
return $result;
}
}
if (count($alpha) > 6) {
if (($alpha[6] - $alpha[2]) == 4) {
$result = $alpha[6];
return $result;
}
}
}
}
Assuming that $cards is an array containing cards values from 1 to 13, I think you need to evaluate with a difference of 4, not 5 :
5 - 1 = 4
6 - 2 = 4
7 - 3 = 4
etc.
You also need to add a specific logic for 10, J, Q, K, A
But for your specific question, what about :
function is_straight(array $cards) {
sort($cards);
if((($cards[4] - $cards[0]) == 4) ||
(($cards[5] - $cards[1]) == 4) ||
(($cards[6] - $cards[2]) == 4)) {
//Code to make sure the cards in between are increment
//is straight.
}
}

Categories