Converting points gained to hours played (Math related) - javascript

I need to use plain JavaScript to convert an amount of experience points to an amount of hours played by a number of fixed rates.
For example:
A player has 1,129,518 experience points.
The amount of experience points that are gained per hour depends on the amount of xp one already has. They would be arranged something like this:
above 0 xp: 8,000 xp/h
above 2,107 xp: 20,000 xp/h
above 101,333 xp: 45,000 xp/h
above 1,210,421 xp: 68,500 xp/h
above 13,034,431 xp: 75,000 xp/h
I'm struggling to find a way to use these xp rates to convert a given amount of experience points to hours played, using at least somewhat elegant Javascript.
I just end up with a cunfusing mess of if/else statements that ends up failing because of math errors.
Any Math wizards out there that can help me? Thanks.
Code Sample: I would go from here
if(xp === 0){
return 0;
}else if( 2107 >= xp > 0){
const result = (xp/8000).toFixed(1);
return result;
}else if(101333 >= xp > 2107){
const result = ((2107/8000) + ((xp-2107)/20000)).toFixed(1);
return result;
}else if(1210421 >= xp > 101333){
...
}
As you can see it would quickly get out of hand if theres alot of different tiers.

First of all, you should write your if statements like this:
if( 2107 >= xp && xp > 0){
...
}
Next, try thinking about XP as buckets of XP and each bucket having different value/price. Go from most valuable bucket to least valuable, and for each bucket calculate hours and subtract amount of XP that was used to calculate those hours.
You can do this in while loop:
let hours = 0;
while(XP > 0)
{
// figure out bucket you are in, your if statements are fine for that.
let value = 0;
let lowerBoundary = 0;
if( 101333 >= xp && xp > 2107){
value = 20000;
lowerBoundary = 2107;
// you need lower boundary to figure out how many XP you used in this bucket.
}
// else if...
const usedInBucket = XP - lowerBoundary;
hours += usedInBucket / value; // simply calculate hours needed
XP -= usedInBucket;
}

This is what I came up with:
const steps = [{
min: 0,
val: 8000
},
{
min: 2107,
val: 20000
},
{
min: 101333,
val: 45000
},
{
min: 1210421,
val: 68500
},
{
min: 13034431,
val: 75000
},
].sort((a, b) => b.min - a.min);
//using for loop
function xpToHours(xp = 0) {
let h = 0;
steps.forEach(s => {
let amt = Math.max(xp - s.min, 0);
h += amt * s.val;
xp -= amt;
});
return h;
}
//using reduce
function xpToHours2(xp = 0) {
return steps.reduce((h, s) => {
let amt = Math.max(xp - s.min, 0);
xp -= amt;
return h + amt * s.val;
}, 0)
}
[0, 1000, 2000, 3000, 1000000].forEach(xp => console.log(xp, xpToHours(xp)));
[0, 1000, 2000, 3000, 1000000].forEach(xp => console.log(xp, xpToHours2(xp)));
To explain:
steps is just an array containing your different stages. It is sorted by the minimum xp from highest to lowest.
Then we just iterate over this array calculating amt which is the xp used up by the currently highest stage. The needed time is therefore amt * currentstep.val and the xp is reduced by the calculated amount for the next stage.

The easiest way to do this is with a sorted array of ranges and Array.prototype.find
// Make sure this is sorted desc
const expRanges = [{
above: 101333,
xph: 45000
},
{
above: 2107,
xph: 20000
},
{
above: 0,
xph: 8000
}
];
function findExpPerHour(xp) {
return expRanges.find(range => range.above < xp).xph;
}
// TESTS
const playerExpTests = [{
name: "P1",
xp: 12
}, {
name: "P2",
xp: 12000
}, {
name: "P3",
xp: 200000
}, {
name: "P4",
xp: 99999999
}];
playerExpTests.forEach(p => {
console.log(p.name, "Exp per hour:", findExpPerHour(p.xp));
});

Related

Recursive Function Uncaught RangeError: Maximum call stack size exceeded

This function which will execute when you enter on input field then it will calculate how much money do you have to return with x type to customer.
But getting stack size error sometime for 6 digit and everytime for 7 digit.
Reproduce: put 1234567 in input box and check the console.
Function:
let returnList = [];
let predictorList = [
100, 50, 20, 10, 5, 1, 0.5, 0.25, 0.1, 0.05, 0.01,
];
let total = 11.23
function isRemainingMoney(value) {
let remainingValue = value;
// when remaning value becomes zero then return the returnlist
if (remainingValue == 0) {
return returnList;
}
for (let pRed of predictorList) {
/* remainingValue is greater than predictor value then push it
eg: 41.33 > 20
21.33 > 20
1.33 > 1
0.33 > 0.25
0.08 > 0.05
0.03 > 0.01 * 3 times
*/
if (remainingValue >= pRed) {
const isPredExist = returnList.find(
(pItem) => +pItem.money == +pRed
);
if (!!isPredExist) {
isPredExist.count += 1;
isPredExist.total = isPredExist.total + +pRed;
} else {
returnList.push({
type: pRed,
money: pRed,
count: 1,
total: pRed,
});
}
remainingValue = +remainingValue.toFixed(2) - pRed;
break;
}
}
// recursive call the same method untill remainivalue becomes zero.
return isRemainingMoney(+remainingValue.toFixed(2));
}
document.querySelector('input').addEventListener('change', (event) => {
if(!!event.target.value) {
returnList.length = 0;
returnList = isRemainingMoney(+event.target.value - total);
console.log(returnList, 'returnList');
}
})
Playground: https://jsbin.com/kuwomalare/edit?html,js,console,output
Current Output From real application:
You end up having way too many recursive calls when the input value is large. It should be fairly straight forward to convert from recursive to iterative using a while loop. The only issue I ran into was floating point not getting down to 0 (like mentioned in the comment by #Keith). When doing money calculations, it is usually best to use integers. Use the smallest denomination in the currency. For example, in US currency, that would be cents. Only convert to decimal (or dollars in this case) when displaying the values.
I have also simplified your calculations a bit. Because of these changes, you could actually use recursion now, if you want since the maximum level of recursion now is predictorList.length.
let predictorList = [
10000, 5000, 2000, 1000, 500, 100, 50, 25, 10, 5, 1
];
let total = 709;
function isRemainingMoney(value) {
let returnList = [];
// when remaning value becomes zero then return the returnlist
while (value != 0) {
for (let pRed of predictorList) {
if (value >= pRed) {
returnList.push({
money: pRed / 100,
count: Math.floor(value / pRed),
});
value %= pRed;
}
}
}
return returnList;
}
document.querySelector('input').addEventListener('change', (event) => {
if (!!event.target.value) {
let returnList = isRemainingMoney(+event.target.value * 100 - total);
console.log(returnList, 'returnList');
}
})
<input type="number">

Optimise Round Up/Down Function

So one of our clients (an auctioneer) has a set of weird increments (also know as London increments), where essentially they don't conform to any divisible number, so using something like: Math.round(number / increment) * increment will not work.
The increments
From: 100, To: 299, Increment: 10
From: 300, To: 319, Increment: 20
From: 320, To: 379, Increment: 30
From: 380, To: 419, Increment: 20
And this kind of thing goes on.
So taking a number like: 311 should round up to 320. Now I have this code and it works fine, it also rounds up/down 321 => 350 and 363 => 380 as expected.
My concern is that it is not fast and/or sustainable and with large numbers that need to be rounded it will get slower. This function needs to be as fast as the Math.round() obviously knowing that it won't but as fast as possible. Now as much as I got it working, the way I have done it is essentially looping X amount of times (x being any number, so I have set it to 9999999, and I am hoping someone knows a better way of doing this.
// Get increment amount
window.getIncrement = (num) => {
var num = parseInt(num);
for (var i = 0; i < window.increments.length; i++) {
if (num >= parseInt(window.increments[i].from) && num <= parseInt(window.increments[i].to)) {
return parseInt(window.increments[i].increment);
}
}
}
// Get increment start value
window.getIncrementStartValue = (num) => {
var num = parseInt(num);
for (var i = 0; i < window.increments.length; i++) {
if (num >= parseInt(window.increments[i].from) && num <= parseInt(window.increments[i].to)) {
return parseInt(window.increments[i].from);
}
}
};
// Custom round up function
const roundToNearestIncrement = (increment, number, roundDown) => {
var incrementStart = parseInt(window.getIncrementStartValue(number));
var increment = parseInt(increment), number = parseInt(number);
console.log(incrementStart, increment, number);
// So now we have a start value, check the direction of flow
var lastBelow = false, firstAbove = false;
for (var i = 0; i < 9999999; i++) {
var incrementRounder = incrementStart + (increment * i);
if (incrementRounder === number) { return number; }
if (incrementRounder < number) { lastBelow = incrementRounder; }
if (incrementRounder > number) { firstAbove = incrementRounder; }
if (lastBelow !== false && firstAbove !== false) { break; }
console.log('Loop #' + i + ', Below: ' + lastBelow + ', Above: ' + firstAbove);
}
return !roundDown ? firstAbove : lastBelow;
}
Then you use it like so:
// Example usage
var num = 329;
var inc = getIncrement(num);
console.log('Rounded: ' + roundToNearestIncrement(inc, num) + ', Expected: 350');
Now as I said it works great, but my concern is that it will slow down a Node process if the number uses something large like 1,234,567, or just the highest number of that increment set, because the code will loop until it finds the above and below number, so if anyone has a better idea on how to do this that it will work but not loop?
See screenshot of the one I did before:
You can see it had to loop 1865 times before it found the above and below amounts.
Anyway, any ideas you have would be appreciated.
There are a couple of ways of making this faster
1.You can store a very big hash will all the possible values and the rounding result. This will use a lot of scape, but will be the fastest. This means that you'll a hash similar to this
rounded = []; rounded[0]=0 ... rounded[100] = rounded[101] = ... = rounded[109] = 110 ... and so on.
Of course this solution depends on the size of the table.
2.Build a binary search tree, based on the breakout points and search that tree. If the tree is balanced it will take O(log(n)) for a search.
If I understand the problem correctly:
Pre-build the array of all the thresholds, in ascending order. I imagine it'll look something like [0, 1, 2,..., 320, 350, 380, 400, 420,...];
Then the lookup will be simple:
const findNearestThreshold = (number) => thresholdsArray
.find(threshold => (threshold >= number));
A solution basing just on the increments array.
const steps = [
{ from: 100, increment: 10}, // I don't need 'to' property here
{ from: 300, increment: 20},
{ from: 320, increment: 30},
{ from: 380, increment: 20},
]
const roundUp = x => {
const tooLargeIndex = steps.findIndex(({from}) => from > x);
const { from, increment } = steps[tooLargeIndex - 1];
const difference = x - from;
return from + Math.ceil(difference / increment) * increment;
}
console.log(300, roundUp(300));
console.log(311, roundUp(311));
console.log(321, roundUp(321));

How do I calculate a blended fee?

I am attempting to calculate an investment management fee using a fee schedule. For example, let's say the investment amount is $5,000,000 and this is the fee schedule:
1,000,000 & Less: 1.00%
2,000,000: 0.75%
3,000,000: 0.50%
3,000,001+: 0.25%
Where dollars under 1,000,000 are charged 1.00% and dollars over $3,000,000 are charged 0.25%. I need to come up with the blended price using javascript (The answer in this case would be 0.55% or $27,500 in dollars).
How do you get the answer if the amount we're using as an example is $5,000,000 and the fee schedule above?
$1,000,000 * 0.0100 = $10,000 (Fee for first $1m)
$1,000,000 * 0.0075 = $7,500 (Fee for second $1m)
$1,000,000 * 0.0050 = $5,000 (Fee for third $1m)
$2,000,000 * 0.0025 = $5,000 (Everything over $3m)
So to manage the $5,000,000, the cost would be the total of the fees above ($27,500). And $27,500 / $5,000,000 = 0.55% which is our "blended fee".
The caveat, is not every fee schedule looks exactly like this. Some have different fees and limits.
What are the results of my research into this problem so far? I have not found a quality example of a function that could accomplish this on SO. It seems all tiered pricing calculations on SO only identify a single price for a given quantity of items, not a blended price though like I am trying to achieve here.
My fee schedule would potentially look something like this array:
fee_schedule: [
{ fee: 0.0100, limit: 1000000},
{ fee: 0.0075, limit: 2000000},
{ fee: 0.0050, limit: 3000000},
{ fee: 0.0025, limit: Infinity}
]
This is the direction I started down, but hit a wall in my head because I saw myself writing a ridiculously long function.
function determineFee(schedule, amount) {
schedule.forEach(tier => {
if (amount <= tier.limit) {
// is this a path I should go down?
}
}
//
}
You could build the delta of the last or zero and actual limit or if it is the last element take Infinity, for using the fee for the rest of the left over value.
Then make a check if the value is smaller then the delta. If true, calculate the fee for the rest value and end the loop.
Otherwise calculate the fee from the delta, decrement the value with the delta and assign the limit as last value, for having this value for the next loop.
function getFee(value) {
var fee = 0;
fee_schedule.some((o, i, a) => {
var delta = i + 1 === a.length
? Infinity
: o.limit - (a[i - 1] && a[i - 1].limit || 0);
if (value < delta) {
fee += value * o.fee;
return true;
}
fee += delta * o.fee;
value -= delta;
});
return fee;
}
var fee_schedule = [{ fee: 0.0100, limit: 1000000 }, { fee: 0.0075, limit: 2000000 }, { fee: 0.0050, limit: 3000000 }, { fee: 0.0025, limit: 4000000 }],
value = 5000000,
fee = getFee(value);
console.log(fee);
For a shorter approach, you could change the data set and use only the slot size of the limits with one million for the first three itmes and Infinity for the last.
function getFee(value) {
var sum = 0;
fee_schedule.some(({ fee, limit }) => {
var v = value < limit ? value : limit;
sum += v * fee;
return !(value -= v);
});
return sum;
}
var fee_schedule = [{ fee: 0.0100, limit: 1000000 }, { fee: 0.0075, limit: 1000000 }, { fee: 0.0050, limit: 1000000 }, { fee: 0.0025, limit: Infinity }],
value = 5000000,
fee = getFee(value);
console.log(fee);
Here is another possibility:
const blended = (schedule) => (amount) =>
schedule.reduce ((sum, {fee, limit}, idx, sched) => {
const top = Math.min (limit, amount)
const bottom = idx == 0 ? 0 : sched[idx - 1].limit
return sum + (top > bottom ? fee * (top - bottom) : 0)
}, 0)
const blendedFee = (schedule) => {
const calc = blended(schedule)
return (amount) => calc(amount) / amount
}
const fee_schedule = [
{ fee: 0.0100, limit: 1000000},
{ fee: 0.0075, limit: 2000000},
{ fee: 0.0050, limit: 3000000},
{ fee: 0.0025, limit: Infinity}
]
console.log (blended (fee_schedule) (5000000))
console.log (blendedFee (fee_schedule) (5000000))
Note that these are curried, so you can store a reference to blended (fee_schedule) or blendedFee (fee_schedule) if you will need to reuse these for various loan amounts.
This does not include the clever early escape of Nina's answer, but I think it's easier to follow.

Coin Change Algorithm JS

I have been trying to come up with a solution for this algorithm for 3-4 days but nothing seems to work and the available solutions are a bit more advanced for me. It has to be solved with conditionals only so no recursion or dynamic programming.
I need to determine the least amount of coins necessary to give change given the following denominations: 1, 0.5, 0.2, 0.1, 0.05, 0.02 and 0.01.
Input is the following:
Price of an item
Sum paid by customer
Current ideas:
let price = +gets();
let paidSum = +gets();
//gets is used to accept number input
let change = paidSum - price;
I figured I could use Math.floor to isolate the integer part and subtract it but then I have no idea what to do with the remaining sum.
Would modulo work to test whether the remaining sum contains any of the remaining values for change and then subtract again until I reach zero?
I do realize this isn't the best formulated question but I am at a loss here and I've done every other task apart from this. Thanks.
Simpler, reverse and map the denominations in cents and return a new array with the number of coins you need for each denomination.
const coinsCents = [1, 2, 5, 10, 20, 50, 100]
const getChange = (amountInCents) => {
return coinsCents.reverse().map(coin => {
let amountCoin = Math.floor(amountInCents/coin)
amountInCents -= amountCoin * coin
return amountCoin
}).reverse()
}
With the denominations you have specified, the problem is simpler than the general change making problem. In this actual case we can be sure that using the largest denomination, that is not greater than the amount to pay, always leads to an optimal solution.
So then there is no need for recursion or dynamic programming. Just a simple loop will do.
I will here ignore the additional "layer" of getting the price of the bill and the amount that the customer pays. In the end the only thing that counts is the change amount to pay back to the customer. So this snippet asks for that change amount and returns the coins that need to be given as change.
function getChange(amount) {
amount *= 100; // Convert to number of cents
var denominations = [1, 2, 5, 10, 20, 50, 100]; // cents
var result = [];
while (amount > 0) {
var coin = denominations.pop(); // Get next greatest coin
var count = Math.floor(amount/coin); // See how many times I need that coin
amount -= count * coin; // Reduce the amount with that number of coins
if (count) result.push([coin/100, count]); // Store count & coin
}
return result;
}
// I/O management
change.oninput = function () {
var coins = getChange(this.value);
result.textContent = coins.map(([coin, count]) => `${count} x $${coin}`).join(" + ");
};
To be paid to customer: <input id="change">
<div>Coins to pay: <span id="result"></span></div>
var coins;
var coinArray = {};
var output = {};
/* Method to get coin value without decimal point - it is required because
* javascript will consider 5.6 as 6 if we do Math.round()
*/
function getRoundFigureCoinValue(x) {
return (x * 10 - ((x * 10) % 10)) / 10;
}
// Method to calculate possible combination of coins
function calculateCoins(input) {
let largestPossibleCoin = 1;
if (input) {
coins.forEach((x) => {
if (input >= x) {
largestPossibleCoin = x;
}
});
let remainingCents = input % largestPossibleCoin;
output[largestPossibleCoin] = getRoundFigureCoinValue(
(input / largestPossibleCoin).toFixed(1)
);
if (remainingCents && input > 1) {
calculateCoins(remainingCents);
}
return largestPossibleCoin;
}
}
// Method to be called to get output.
function calculatePossibleCoinCombinations(value) {
if (isNaN(value) || +value <= 0) {
console.log('Invalid input');
return;
} else {
console.log('Possible combinations are:')
value = +value;
}
coins = [1, 5, 10, 25];
while (coins.length) {
let largestPossibleCoin = calculateCoins(value) || 0;
let outputString = '';
coins = coins.filter((x) => x < largestPossibleCoin);
Object.keys(output).forEach((key) => {
outputString += `${output[key]} - ${key} cents; `;
})
console.log(outputString);
output = {};
}
}
/*
Sample inputs:
calculatePossibleCoinCombinations('89');
calculatePossibleCoinCombinations(10);
calculatePossibleCoinCombinations(0);
calculatePossibleCoinCombinations('someString');
calculatePossibleCoinCombinations(-10)
*/

Calculating tier price

This is the example data:
100 items or less = $20
200 items or less = $15
500 items or less = $10
Example scenario:
user inputs 150 items -> price is $15 per item
And this is how far I get:
http://jsfiddle.net/ByPh5/
<script type="text/javascript">
$(function(){
var tier_prices = {
'100':'20',
'200':'15',
'500':'10'
}
var user_input = $('#user_input').val();
var price_output = 0;
/*
calculate
*/
$('#price_output').text(price_output)
})​
</script>
<input type="text" id="user_input" value="150"/>
<p id="price_output"></p>​
any help is much appreciated
(Note: Since you left some ambiguity, I'll assume that 500+ items also cost $20.)
Instead of messing with that data-structure, you can do something simpler. First the code, then the explanation (if the comments aren't enough.)
function determine_price ( qty ) {
var prices = [
20, //0 to 99
20, //100 to 199
15, //200 to 299
15, //300 to 399
15, //400 to 499
10 //500+
];
//divide by the common denominator
//0-99 turn into 0,
//100-199 turns into 1
//200-299 turns into 2
//and so on
qty = Math.floor( qty / 100 );
//check for 600+, which are the same as 500 (last array item)
return prices[ qty ] || prices.pop();
}
100, 200 and 500 have something in common: They're multiples of 100. So we take an array, and treat each element as if it's a range of 100: the first element (0) is 0 to 99 items, second element (1) is 100 to 199 items and so forth. Then, for each input quantity, we divide by that common denominator, to find out in which range it falls, and grab the price corresponding to that quantity.
In the case of ambiguity, which is what happens for 600+ elements (the last element, element #6 is for 500-599), we simply take the last range's price.
No need for loops, just simple math.
First, instead of specifying the max quantity for a price tier, specify the min quantity. And define it as a sorted array so you can iterate through it.
var tier_prices = [
{ minQty: 0, unitPrice: 20 },
{ minQty: 101, unitPrice: 15 },
{ minQty: 201, unitPrice: 10 }
];
Then, loop through the values until you get to a minimum quantity that is greater than the entered quantity:
var qty = +$('#user_input').val();
var price;
for (var i = 0; i < tier_prices.length && qty >= tier_prices[i].minQty; i++) {
price = tier_prices[i].unitPrice;
}
$('#price_output').text(price * qty);
http://jsfiddle.net/gilly3/ByPh5/3/
Objects are nice but a bit annoying since you're not guaranteed to go through the values in order.
http://jsfiddle.net/radu/6MNuG/
$(function() {
var tier_prices = {
'100': '20',
'200': '15',
'500': '10'
};
$('#user_input').change(function() {
var num = parseInt($(this).val(), 10),
price = 0,
prevTier = 0,
maxTier = 0;
for (var tier in tier_prices) {
if (tier_prices.hasOwnProperty(tier) && num <= tier) {
if (tier < prevTier || prevTier == 0) {
price = tier_prices[tier];
prevTier = tier;
}
}
if (tier > maxTier) maxTier = tier;
}
if (num > maxTier) price = tier_prices[maxTier];
$('#price_output').text(price * num);
});
})​;​
Example with a multidimensional array: http://jsfiddle.net/radu/6MNuG/
$(function() {
var tier_prices = [
[100, 20],
[200, 15],
[500, 10]
];
$('#user_input').change(function() {
var num = parseInt($(this).val(), 10),
price = 0,
n = tier_prices.length - 1;
if (num > tier_prices[n][0]) {
price = tier_prices[n][1];
} else {
for (var i = 0; i <= n; i++) {
if (num <= tier_prices[i][0]) {
price = tier_prices[i][1];
break;
}
}
}
$('#price_output').text(price * num);
});
});​
Try:
var tier_prices = {
'100': '20',
'200': '15',
'500': '10'
}
var price_output = 0;
var multiplier = 1;
$('#user_input').change(function() {
var user_input = parseInt($('#user_input').val(),10);
for (tier in tier_prices) {
if (user_input <= tier) {
multiplier = tier_prices[tier];
break;
}
}
$('#price_output').text(user_input * multiplier);
});​
jsFiddle example
UPDATE
Here's an example forgoing the object you had with a simple switch/case since the idea of the object isn't very popular or functional. Note that I added a case for quantities greater than 500:
$('#user_input').change(function() {
var user_input = parseInt($('#user_input').val(), 10);
switch (true) {
case user_input >= 0 && user_input <= 100:
$('#price_output').text(user_input * 20);
break;
case user_input > 100 && user_input <= 200:
$('#price_output').text(user_input * 15);
break;
case user_input > 200 && user_input <= 500:
$('#price_output').text(user_input * 10);
break;
case user_input > 500:
$('#price_output').text(user_input * 5);
}
});​
jsFiddle example
Was looking for a similar thing and decided to do it slightly different, ran it in js fiddle and it seems to work pretty well. As gilly3 pointed out you probably don't want an upper limit, so you might want to do '1 or more, 100 or more...500 or more'. The vars and div names I used were different but you can see what I'm trying to do and adapt it to your needs:
JSFiddle: https://jsfiddle.net/vx4k2vdh/5/
(function() {
const tiers = {
0: 20,
100: 15,
200: 10,
500: 5
}
/**
* Take qty and return the first appropriate
* tier that it encounters, break when
* tier has been identified so you don't
* waste time iterating if u've already found tier
**/
function calculatePriceTier(qty) {
var selectedTier;
for (var tier in tiers) {
if (tiers.hasOwnProperty(tier)) {
if (qty < tier) break;
selectedTier = tier;
}
}
return selectedTier;
}
$(function() {
/**
* Every time a new number is selected
* run calculations and grab tier, total
**/
$('#items').on('input', 'input', function() {
var qty = +$(this).val(),
tier = calculatePriceTier(qty),
total = qty * tiers[tier];
$('#total-price span').text(total);
});
});
})();
What is described in the OP's request is volume pricing.
All the examples given here are for volume pricing not tier pricing.
Tire pricing example in ruby
https://repl.it/repls/IndigoRightDisks
def cost(qty)
tier = {
10 => 100,
50 => 97,
100 =>82,
200 =>71,
300 =>66,
400 =>64,
500 =>27,
1000 =>12
}
cs = []
for cnt in 1..qty
d = tier.keys.find{|x| cnt <= x ?cnt <= x : tier.keys.last == x }
cs << tier[d]
end
return cs.reduce{|y,x| y+x};
end

Categories