leveling up PHP algorithm - php

I want some algorithm to make a PHP level up system I already made a system to level up people but what if people gained a huge amount of exp so they need to level up multiple times example of my code:
if(isset($_POST['id']) && isset($_POST['amount'])) {
$amount = $_POST['amount'];
$user = $_POST['id'];
$exp;$etnl;$newExp;$level;
`//select exp and etnl from DB
$result = DB::getInstance() ->query("SELECT exp,etnl,level FROM exp WHERE id = $user");
$result->setFetchMode(PDO::FETCH_ASSOC);
$row = $result->fetch();
$exp = $row['exp'];
$etnl = $row['etnl'];
$level = $row['level']
//c where to level up user or to add exp to his ep bar
if($amount + $exp > $etnl) {
$etnl = $etnl * 2;
$leftOvers = ($amount + $exp) - $etnl;
$newExp = $leftOvers;
$level = $level + 1;`
$result = DB::getInstance()->prepare("UPDATE exp SET exp=?,level=?,etnl=? WHERE id=?");
$result->execute(array($amount,$level,$etnl,$user));
} else {
$result = DB::getInstance()->prepare("UPDATE exp SET exp=? WHERE id=?");
$result->execute(array($amount,$user));
}
}`
`
but this system fails if the exp could level up a person 2 times any help or any new code? BTW etnl stands for exp till next level thank you

If you don't want to manually type out a level table like the other answer suggets you can also try doing your process with a recursive function:
$current_exp = 0;
$current_level = 1;
$to_next_level = 1000;
function did_level( $amount = 0 ) {
global $current_exp;
global $curent_level;
global $to_next_level;
$tmp_amount = $amount + $current_exp;
$tmp_amount_remainder = $tmp_amount - $to_next_level;
if ($tmp_amount >= $to_next_level) {
$current_level++;
$to_next_level *= 2;
$current_exp = $tmp_amount;
if ($tmp_amount_remainder > 0) {
// Level us up again.
did_level( $tmp_amount_remainder );
} else {
// Store the new level, to next level, and current exp
}
} else {
// Store the new exp amount
}
}

You have to get a table of levels which is basically a list of [exp, level] pairs. For example,
$level_list = [
['exp' => 0, 'level' => 1],
['exp' => 100, 'level' => 2],
['exp' => 1000, 'level' => 3],
// etc.
];
Then, you iterate the table starting from the end:
$i = count($level_list);
while($i > 0) {
$i--;
if ($user_exp >= $level_list[i]['exp']) {
// this is the level we need
break;
}
}
$target_level = $level_list[$i]['level'];
$target_exp = $level_list[$i]['exp'];

Related

How to determine if data is increasing or decreasing in PHP

Let's say we have the following data in an array:
$data1 = [3,5,7,6,8,9,13,14,17,15,16,16,16,18,22,20,21,20];
$data2 = [23,18,17,17,16,15,16,14,15,10,11,7,4,5];
As with $data1 we can say the data is increasing while in $data2 it is decreasing.
Using PHP, how do you know the data is increasing or decreasing, and is there a way on how to measure
know the rate of increasing as well as decreasing i.e in terms of percentage.
Edit
From the comments I received I got an idea and here is what I have tried.
What I want to achieve;
I want to know if the trend of the data coming in is upwards or downwards.
Want also to know the rate at which the data is rising or droping. For example $data1 = [1,3,5]; is not the same as $data2 = [1, 20, 55];. You can see $data1 rate of increase is not the same as $data2.
function increaseOrDecrease($streams = []) : array
{
$streams = [3,5,7,6,8,9,13,14,17,15,16,16,16,18,22,20,21,20]; // For the increasing
//$streams = [23,18,17,17,16,15,16,14,15,10,11,7,4,5]; // For the decreasing
$first = 0;
$diff = [];
foreach ($streams as $key => $number) {
if ($key != 0) {
$diff[] = $number - $first;
}
$first = $number;
}
$avgdifference = array_sum($diff)/count($diff); //Get the average
$side = $avgdifference > 0 ? 'UP' : 'DOWN';
$avgsum = array_sum($streams)/count($streams);
$percentage = abs($avgdifference)/$avgsum * 100;
if ($side == 'UP') {
$data = [
'up' => true,
'percent' => $percentage,
];
}else {
$data = [
'up' => false,
'percent' => $percentage,
];
}
return $data;
}
I would like some help to refactor this code or the best approach to solve the issue.
There are several ways to analyze data and extract a trend. The most classical method is called
least squares. It's a way of fitting a line
through the data. The method computes the slope and the intercept of the line. The trend is just the slope.
The formulas are given here.
A PHP implementation is the following:
function linearRegression($x, $y)
{
$x_sum = array_sum($x);
$y_sum = array_sum($y);
$xy_sum = 0;
$x2_sum = 0;
$n = count($x);
for($i=0;$i<$n;$i++)
{
$xy_sum += $x[$i] * $y[$i];
$x2_sum += $x[$i] * $x[$i];
}
$beta = ($n * $xy_sum - $x_sum * $y_sum) / ($n * $x2_sum - $x_sum * $x_sum);
$alpha = $y_sum / $n - $beta * $x_sum / $n;
return ['alpha' => $alpha, 'beta' => $beta];
}
function getTrend($data)
{
$x = range(1, count($data)); // [1, 2, 3, ...]
$fit = linearRegression($x, $data);
return $fit['beta']; // slope of fitted line
}
Examples:
echo getTrend([1, 2, 3]); // 1
echo getTrend([1, 0, -1]); // -1
echo getTrend([3,5,7,6,8,9,13,14,17,15,16,16,16,18,22,20,21,20]); // 1.065
echo getTrend([23,18,17,17,16,15,16,14,15,10,11,7,4,5]); // -1.213
You are asking for a type of data structure that can represent ascending as well as descending data. PHP got SplMinHeap and SplMaxHeap for this purpose. These built in classes make life easer when dealing with ascending or descending datasets.
A quick example ...
<?php
declare(strict_types=1);
namespace Marcel;
use SplMinHeap;
$numbers = [128, 32, 64, 8, 256];
$heap = new SplMinHeap();
foreach ($numbers as $number) {
$heap->insert($number);
}
$heap->rewind();
while($heap->valid()) {
// 8, 32, 64, 128, 256
echo $heap->current() . PHP_EOL;
$heap->next();
}
The SplMinHeap class keeps the minimum automatically on the top. So just use heaps instead of arrays that have no structure. Same goes for SplMaxHeap that keeps the highest value on the top.
Finding the differences
If you want to iterate all data and finding the differences between one to the next, you just have to iterate the heap. It 's ordered anyway.
$heap->rewind();
$smallest = $heap->current();
while($heap->valid()) {
// 8, 32, 64, 128, 256
$current = $heap->current();
echo $current . PHP_EOL;
// 0 (8 - 8), 24 (32 - 8), 32 (64 - 32), 64 (128 - 64), 128 (256 - 128)
echo "difference to the value before: " . ($current - $smallest) . PHP_EOL;
$smallest = $current;
$heap->next();
}
I would do simple things like this
$data1 = [3,5,7,6,8,9,13,14,17,15,16,16,16,18,22,20,21,20];
$data2 = [23,18,17,17,16,15,16,14,15,10,11,7,4,5];
getTrend($data1) //Returns up
getTrend($data2) // Returns down
function getTrend($arr)
{
$up = 0;
$down = 0;
$prev = "";
foreach($arr as $val)
{
if($prev != "" && $val > $prev)
{
$up = $val-$prev;
}
else if($prev != "" && $val < $prev)
{
$down = $prev-$val ;
}
$prev = $val);
}
if($up > $down)
{
return "up";
}
else if($down > $up)
{
return "down";
}
else {
return "flat";
}
}

How to organize in chunks a not accurate division?

I have a number of participants and a number of groups, and I have to organize the participants into groups.
Example:
10/3 = 3, 3 and 4.
10/9 = 2,2,2 and 4.
23/3 = 6,6,6 and 5.
I have tried with array_chunk using the size paramether as a rounded result of participants/groups but it Did not work well.
Edit with my problem solved.
$groups = $this->request->data['phases_limit'];
$classified_lmt = $this->request->data['classified_limit'];
$participants = count($game->user_has_game);
$participants_lmt = floor($participants / $groups);
$remainders = $participants % $groups;
if ($groups > $participants) {
throw new \Exception("Há mais grupos que participantes");
}
for ($i=0; $i < $groups; $i++) {
$p = $this->Phase->newEntity();
$p->name = 'Grupo #' . $game->id;
$p->game_id = $game->id;
$p->classified_limit = $classified_lmt;
$this->Phase->save($p);
// add the number of participants per group
for ($j=0; $j < $participants_lmt; $j++) {
$user_has_game = array_pop($game->user_has_game);
$g = $this->Phase->GroupUserHasGame->newEntity();
$g->group_id = $p->id;
$g->user_has_game_id = $user_has_game->id;
$this->Phase->GroupUserHasGame->save($g);
}
// check if it is the last iteration
if (($groups - 1) == $i) {
// add the remainders on the last iteration
for ($k=0; $k < $remainders; $k++) {
$user_has_game = array_pop($game->user_has_game);
$g = $this->Phase->GroupUserHasGame->newEntity();
$g->group_id = $p->id;
$g->user_has_game_id = $user_has_game->id;
$this->Phase->GroupUserHasGame->save($g);
}
}
}
Have you tried the modulus operator? It gives you the remainder after dividing the numerator by the denominator.
For example, if you want to split 10 people into 3 groups:
floor(10 / 3) = 3; // people per group
10 % 3 = 1; // 1 person left over to add to an existing group.
Edit - I included the following function as part of my original answer. This doesn't work for OP, however I want to leave it here, as it may help others.
function group($total, $groups)
{
// Calculate participants per group and remainder
$group = floor($total / $groups);
$remainder = $total % $groups;
// Prepare groupings and append remaining participant to first group
$groupings = array_fill(0, $groups, $group);
$groupings[0] += $remainder;
return $groupings;
}
Not sure there are off-the-shelf libraries for that. I just implemented something similar in Java if you need some ideas:
public List<Integer> createDistribution(int population_size, int groups) {
List<Integer> lst = new LinkedList();
int total = 0;
for (double d : createDistribution(groups)) {
// this makes smaller groups first int i = new Double(population_size * d).intValue();
int i = (int)Math.round(population_size * d);
total += i;
lst.add(i);
}
// Fix rounding errors
while (total < population_size) {
int i = r.nextInt(groups);
lst.set(i, lst.get(i) + 1);
total += 1;
}
while (total > population_size) {
int i = r.nextInt(groups);
if (lst.get(i) > 0) {
lst.set(i, lst.get(i) - 1);
total -= 1;
}
}
return lst;
}

Retrieve All Data From One Table in Database into Array and Calculate it

Related to question from this Pearson correlation in PHP , now I'm using that function too for calculate similarity between 2 users with pearson correlation. What I wanna ask is :
I have database name ta_db
And I want to retrieve data from table interest which have 9 attributes into array
After that I want to calculate it with pearson correlation function.
public function similarity($user1, $user2) {
$sharedItem = array();
$pref1 = array();
$pref2 = array();
$result1 = $user1->fetchAllPreferences();
$result2 = $user2->fetchAllPreferences();
foreach($result1 as $pref){
$pref1[$pref->item_id] = $pref->rate;
}
foreach($result2 as $pref){
$pref2[$pref->item_id] = $pref->rate;
}
foreach ($pref1 as $item => $preferenza){
if(key_exists($item,$pref2)){
$sharedItem[$item] = 1;
}
}
$n = count($sharedItem);
if ($n == 0) return 0;
$sum1 = 0;$sum2 = 0;$sumSq1 = 0;$sumSq2 = 0;$pSum = 0;
foreach ($sharedItem as $item_id => $pre) {
$sum1 += $pref1[$item_id];
$sum2 += $pref2[$item_id];
$sumSq1 += pow($pref1[$item_id],2);
$sumSq2 += pow($pref2[$item_id],2);
$pSum += $pref1[$item_id] * $pref2[$item_id];
}
$num = $pSum - (($sum1 * $sum2) / $n);
$den = sqrt(($sumSq1 - pow($sum1,2)/$n) * ($sumSq2 - pow($sum2,2)/$n));
if ($den == 0) return 0;
return $num/$den;
}
From my explanation above, could you please tell me how to retrieve the data into Array? Thank you in advance for your help and clear explanation.

Incrementing through a MYSQL result set without stopping

I am trying to increment through a result set from my table.
I attempt to display the next three results using an increment. This is working fine.
For example;
current = 5.
It then displays the next three: 6,7,8
It can also display the previous three: 4,3,2
The problem comes when I reach the last couple or minimum couple of results. It will currently stop;
current = 23
next: 24, 25
I cannot figure out to loop through to the last or first few results.
E.g. I want it to do this:
current = 2
display previous three: 1, 25, 24
AND FOR next:
current = 23:
display next three: 24, 25, 1
I'm returning these as arrays. Code:
$test_array = array();
$no = 1;
while($results=mysql_fetch_assoc($test_query))
{
$test_array[] = array('test_id' => $results['id'],
'path' => $results['Path'],
'type' => $results['FileType']
'no' => $no);
$no++;
}
$current_no = 0;
if(is_array($test_array) && count($test_array)>0)
{
foreach($test_array as $key=>$array)
{
if($array['test_id']==intval($db_res['current_id']))
{
$current[] = $array;
$current_no = intval($key+1);
}
else
//error
if(intval($current_no)>0)
{
//Next 3
for($i=0;$i<3;$i++)
{
if(isset($test_array[intval($current_no+$i)]) && is_array($test_array[intval($current_no+$i)]))
{
$next[] = $test_array[intval($current_no+$i)];
}
else
break;
}
//Previous 3
for($i=2;$i<5;$i++)
{
if(isset($test_array[intval($current_no-$i)]) && is_array($test_array[intval($current_no-$i)]))
{
$previous[] = $test_array[intval($current_no-$i)];
}
else
break;
}
break;
}
else
//error
}
}
else
//error
If anyone has any ideas on how to help, that would be great!
Find $key for $current and set $next_key = $prev_key = $key;
Find last key $max = count($test_array) - 1;
Increase $next_key & decrease $prev_key 3 times (and check if
boundary is reached):
Loop might look like this.
for ($i = 1; $i <= 3; $i++)
{
$next_key = ($next_key == $max) ? 0 : $next_key + 1;
$prev_key = ($prev_key == 0) ? $max : $prev_key - 1;
$next[] = $test_array[$next_key];
$prev[] = $test_array[$prev_key];
}

Split total into 5 different numbers

Ok what i'm wanting to do is split a number ($row['count']) into 5, this is easy enough if you want equal numbers:
$sum = ($row['count'] / 5);
$fsum = floor($sum);
but I want each number to be different and still add up to total ie $row['count'] how can this be achieved?
Update:
If this helps its to be used to update 5 rows in a database:
$query = "SELECT * FROM foo";
$result = mysql_query($query);
while ($row = mysql_fetch_array($result)) {
$sum = ($row['count'] / 5);
$fsum = floor($sum);
$id = $row['id'];
$update = "UPDATE foo SET foo1='$fsum', foo2='$fsum', foo3='$fsum', foo4='$fsum', foo5='$fsum' WHERE id='$id'";
mysql_query($update);
}// while
so ideally the $update query would be something like:
$update = "UPDATE foo SET foo1='$fsum1', foo2='$fsum2', foo3='$fsum3', foo4='$fsum4', foo5='$fsum5' WHERE id='$id'";
This is my take:
function randomize($sum, $parts) {
$part_no = count($parts);
$continnue_counter = 0;
while (count(array_unique($parts)) != $part_no) {
$changing = array_rand($parts, 2);
if (($parts[$changing[0]] - 1) == 0 || ($parts[$changing[1]] - 1) == 0) { // don't let them go under 1
++$continnue_counter;
// sometime one element get everything and others even out on 1
// just throw away everything you got so far and start over
if ($continnue_counter > 10) {
$parts = setup($sum, $part_no);
$continnue_counter = 0;
}
continue;
}
$continnue_counter = 0;
$signum = mt_rand(0, 100) % 2 ? 1 : -1;
$delta = $signum * mt_rand(1, min($parts[$changing[0]] - 1, $parts[$changing[1]] - 1)); // -1 to make sure they don't go under 0
$parts[$changing[0]] += $delta;
$parts[$changing[1]] -= $delta;
}
return $parts;
}
function setup($sum, $part_no) {
$parts = array_fill(0, $part_no, (int)($sum / $part_no));
// acount for the reminder of (int) cast
$reminder = $sum - array_sum($parts);
while ($reminder) {
$parts[array_rand($parts)] += 1;
--$reminder;
}
return $parts;
}
$part_no = 5;
$sum = 42;
$parts = randomize($sum, setup($sum, $part_no));
var_export($parts);
print array_sum($parts)
Update:
I've added a version that introduces a little more entropy.
Update2:
The more random one had a tendency to decrement everything to 1 except one part, added an explicit detection to deal with this. Still the algorithm behind it has unknown termination time.

Categories