Home > Back-end >  Calculate number of medals based on number of entries
Calculate number of medals based on number of entries

Time:09-18

I have an app that awards the user based upon number of entries made.

  • 10 entries = 1 bronze,
  • 20 entries = 1 bronze, 1 silver,
  • 30 entries = 1 bronze, 1 silver and 1 gold

It then repeats the cycle forever, always in multiples of 10.

  • 40 entries = 2 bronze, 1 silver, 1 gold
  • 50 entries = 2 silver, 2 silver, 1 gold
  • 60 entries = 2 gold, 2 bronze and 2 gold

1-9 should show in progress for each level, and every tenth entry begins the next level. 1 is 10%, 9 is 90 % and rather than show 100%, just go straight to the next level.

I show a progress bar for the current level, a percentage of complete for current level, the icon of the level the user is going for. All of those currently show correctly.

The issue I'm facing is I also show a count for each level, which is only correct up to and including 44 entries. The 45th is where things start to go wrong.

Please check out the below or on jsfiddle and feel free to edit.

const DENOMINATOR = 10;

const clamp = (value: number, min: number, max: number): number => {
    return Math.min(Math.max(value, min), max);
}

const calculateAwardsByEntryCount = (count: number) => {
  const maxLevels = 3 * DENOMINATOR;
  const modulus = count % maxLevels;
  const currentLevel = Math.max(0, Math.floor(modulus / DENOMINATOR) - 1);
  const nextLevel = Math.min(3, currentLevel   1);
  const prestige = Math.ceil((count   1) / maxLevels);
  const progress = ((modulus - DENOMINATOR * nextLevel) / DENOMINATOR) * 100;

  let bronze = 0;
  let silver = 0;
  let gold = 0;

  if (count >= DENOMINATOR) {
    bronze = count <= maxLevels ? 1 : Math.floor(modulus / (DENOMINATOR - 1)) * prestige;
  }

  if (count >= DENOMINATOR * 2 && bronze > 0) {
    silver = count <= maxLevels ? 1 : clamp(Math.ceil(modulus / (DENOMINATOR * 2 - 1)) * prestige, 0, bronze   1);
  }

  if (count >= DENOMINATOR * 3 && silver > 0) {
    gold = count <= maxLevels ? 1 : clamp(Math.ceil((modulus / (DENOMINATOR * 3 - 1)) * prestige), 0, silver   1);
  }

  return {
    achieved: {bronze, silver, gold},
    currentLevel,
    nextLevel,
    progress,
  }
}

console.log(calculateAwardsByEntryCount(45));

CodePudding user response:

It's not exactly the most efficient solution, but at least it 100% accurate for any number of entries.

const DENOMINATOR = 10;

const calculateAwardsByEntryCount = (count: number) => {
  let remainingProgress = count;
  let currentLevel = null;
  let bronze = 0;
  let silver = 0;
  let gold = 0;

  for (remainingProgress; remainingProgress >= DENOMINATOR; remainingProgress -= DENOMINATOR) {
    switch (currentLevel) {
      case 1:
        currentLevel = 2;
        gold  = 1;
        break;
      case 0:
        currentLevel = 1;
        silver  = 1;
        break;
      default:
        currentLevel = 0;
        bronze  = 1;
        break;
    }
  }


  if (currentLevel === null) {
    currentLevel = -1
  }
  
  const progress = (remainingProgress / DENOMINATOR) * 100;
  const nextLevel = currentLevel === 2 ? 0 : currentLevel   1;

  return {
    achieved: {
      bronze,
      silver,
      gold
    },
    nextLevel,
    progress,
  };
}

console.log(calculateAwardsByEntryCount(9));
console.log(calculateAwardsByEntryCount(10));
console.log(calculateAwardsByEntryCount(20));
console.log(calculateAwardsByEntryCount(30));
console.log(calculateAwardsByEntryCount(35));
console.log(calculateAwardsByEntryCount(45));
console.log(calculateAwardsByEntryCount(55));
console.log(calculateAwardsByEntryCount(65));
console.log(calculateAwardsByEntryCount(75));

  • Related