Jguy
Jguy

Reputation: 580

PHP dividing a variable by multiple multi-dimensional arrays to produce no remainder

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; ?>

Upvotes: 0

Views: 56

Answers (2)

Paul
Paul

Reputation: 9012

$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 . ')' . "\r\n";
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.

Upvotes: 1

i alarmed alien
i alarmed alien

Reputation: 9530

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.

Upvotes: 1

Related Questions