The project I have is a reimbursement of materials in a game. The materials come in various packages listed in the array below. Part of my code gives me the total number of units I need to reimburse, and I have to use the "packages" from the array below.
This is the multi dimensional array. The $item['ore'][100] array indicates that the item listed gives 100 units when used and stacks to only 100 (key => stacksize). Likewise for $item['ore'][500] gives 500 units when used and only stacks to 100:
$item = array(
'ore' => array(
100 => array(
'name' => "Item 1",
'stacksize' => 100,
),
500 => array(
'name' => "Item 2",
'stacksize' => 100,
),
1000 => array(
'name' => "Item 3",
'stacksize' => 100,
),
),
So, 1x Item 1 gives 100 units, 1x Item 2 gives 500 units and 1x Item 3 gives 1000 units. My $totalOre variable from part of my code dictates that I have 15,825 units to reimburse total (as an example). Obviously I'll have some extra that will be reimbursed as I don't have an item that gives smaller than 100 units (not a big deal if the user gets a bit more in this case).
So I need a code snippet to tell me that I need to reimburse 15x Item 3, 1x Item 2 and 4x Item 1 for a total of 15,900 units.
Additionally, if I have say....$totalOre = 219,427, I need my code to dictate that I send 100x Item 3, then 100x Item 3, then 19x Item 3, then 1x Item 2 for a total of 219,500 units, because of the 'stacksize' limitation of 100 (I can't send a user more than 100x of these items or they won't get them).
I assume some sort of loop would be needed in addition the Modulus (%) operator. I tried
$reimburseOre = 0;
$reimburseOre = $reimburseOre % $items['ore'][1000];
and came up with 0. Any assistance you can provide would be greatly appreciated, even just a hand in the right direction.
Thank you.
EDIT: Tried adding a 'Qty' key to my array (set to 1000) and then the following snippet of code and the result was -523 (not correct at all):
$oreReimbursement = 0;
if ($totalOre % $items['ore'][1000]['Qty'] != 0) {
$oreReimbursement -= $totalOre % $items['ore'][1000]['Qty'];
}
echo $oreReimbursement; ?>
Here's some code to get you on your way. You'll have to work out the stacks, but this should help with knowing what combination of ore lots you have to repay.
# $target is the amount of ore to be repaid
$target = 481726;
# first, reverse sort the keys of 'ore' so they're in descending numerical order
krsort( $item['ore'] );
# initialise an array to contain our "order" of ore.
$order = [];
# for every value of ore, starting with the largest amount
foreach ( $item['ore'] as $a => $v ) {
# if the target to be reached is larger than the amount, $a
if ($target > $a) {
# floor( $target / $a ) means round down $target/$a to the nearest integer
# add the name (Item x) and the rounded down number to the order.
$order[ $v["name"] ] = floor($target / $a);
# reset $target to the remainder
$target = $target % $a;
}
}
# if we still have some ore left to give, add one lot of the smallest ore quantity
if ($target) {
( isset($order[ $v["name"] ]) )
? $order[ $v["name"] ]++
: $order[ $v["name"] ] = 1;
}
print_r($order);
Output for 481726:
Array
(
[Item 3] => 481
[Item 2] => 1
[Item 1] => 3
)
You should be able to work out how to divide the 485 stacks into sets of 100.
$totalOre = 15825;
$totalWood = 219427;
reimburse($item['ore'], $totalOre);
reimburse($item['wood'], $totalWood);
function reimburse($units, $total) {
$unitSizes = array_keys($units);
rsort($unitSizes); // order from high to low
$temp = $total;
$actual = array();
$reimbursed = 0;
foreach($unitSizes AS $unitSize) {
$stacks = floor($temp / $unitSize);
$stacksize = $units[$unitSize]['stacksize'];
$itemName = $units[$unitSize]['name'];
while($stacks > $stacksize) {
$actual[$itemName] += $stacksize;
$reimbursed += $stacksize * $unitSize;
$stacks -= $stacksize;
$temp -= $stacksize * $unitSize;
}
if ($stacks > 0) {
$actual[$itemName] += $stacks;
$reimbursed += $stacks * $unitSize;
$temp -= $stacks * $unitSize;
$stacks = 0;
}
}
if ($temp > 0) {
$actual[$itemName]++;
$reimbursed += $unitSize;
}
print 'Total: ' . $reimbursed . ' (' . $total . ')' . "
";
print_r($actual);
}
Output:
Total: 15900 (15825)
Array
(
[Item 3] => 15
[Item 2] => 1
[Item 1] => 4
)
Total: 219500 (219427)
Array
(
[Item 6] => 219
[Item 4] => 5
)
See the demo.
As you can see, it has one flaw: instead of 5 times Item 4 in the wood example it should be one Item 5. To get around that, one needs to determine the final total reimbursement first.
sort($unitSizes); // order from low to high to get lowest unitsize
$smallestUnit = $unitSizes[0];
$total = ceil($total / $smallestUnit) * $smallestUnit;
See the demo.