code icon Code

RICE/ICE Calculator

Calculate prioritization scores using RICE or ICE frameworks

Source Code

import { readFileSync, writeFileSync } from "fs";

const [inputFile, outputFile, sensitivity] = process.argv.slice(2);

// Read input data
const input = JSON.parse(readFileSync(inputFile, "utf-8"));
const framework = input.framework?.toLowerCase() || "rice";
const initiatives = input.initiatives || [];

if (initiatives.length === 0) {
  console.log(JSON.stringify({ error: "No initiatives provided" }));
  process.exit(1);
}

// Calculate scores based on framework
function calculateRICE(initiative) {
  const { reach, impact, confidence, effort } = initiative;
  if (!reach || !impact || !confidence || !effort) {
    return { score: 0, error: "Missing required fields" };
  }
  const score = (reach * impact * confidence) / effort;
  return {
    score: Math.round(score * 100) / 100,
    breakdown: {
      reach,
      impact,
      confidence,
      effort,
      formula: `(${reach} × ${impact} × ${confidence}) / ${effort}`,
    },
  };
}

function calculateICE(initiative) {
  const { impact, confidence, ease } = initiative;
  if (!impact || !confidence || !ease) {
    return { score: 0, error: "Missing required fields" };
  }
  const score = impact * confidence * ease;
  return {
    score: Math.round(score * 100) / 100,
    breakdown: {
      impact,
      confidence,
      ease,
      formula: `${impact} × ${confidence} × ${ease}`,
    },
  };
}

// Score all initiatives
const scored = initiatives.map((initiative) => {
  const calc = framework === "ice" ? calculateICE(initiative) : calculateRICE(initiative);
  return {
    name: initiative.name,
    description: initiative.description || "",
    ...calc,
  };
});

// Sort by score descending
scored.sort((a, b) => b.score - a.score);

// Add rankings
const ranked = scored.map((item, index) => ({
  rank: index + 1,
  ...item,
}));

// Sensitivity analysis (if requested)
let sensitivityAnalysis = null;
if (sensitivity === "true" && framework === "rice") {
  sensitivityAnalysis = ranked.slice(0, 3).map((item) => {
    const original = initiatives.find((i) => i.name === item.name);
    if (!original) return null;

    // What if effort is 50% higher?
    const effortHigher = calculateRICE({ ...original, effort: original.effort * 1.5 });
    // What if reach is 30% lower?
    const reachLower = calculateRICE({ ...original, reach: original.reach * 0.7 });
    // What if confidence drops to 50%?
    const lowConfidence = calculateRICE({ ...original, confidence: 0.5 });

    return {
      name: item.name,
      originalScore: item.score,
      scenarios: {
        "effort +50%": {
          newScore: effortHigher.score,
          change: `${Math.round(((effortHigher.score - item.score) / item.score) * 100)}%`,
        },
        "reach -30%": {
          newScore: reachLower.score,
          change: `${Math.round(((reachLower.score - item.score) / item.score) * 100)}%`,
        },
        "confidence → 50%": {
          newScore: lowConfidence.score,
          change: `${Math.round(((lowConfidence.score - item.score) / item.score) * 100)}%`,
        },
      },
    };
  }).filter(Boolean);
}

// Build output
const output = {
  framework: framework.toUpperCase(),
  initiativeCount: ranked.length,
  ranked,
  ...(sensitivityAnalysis && { sensitivityAnalysis }),
  generatedAt: new Date().toISOString(),
};

// Write output
writeFileSync(outputFile, JSON.stringify(output, null, 2));

console.log(`Scored ${ranked.length} initiatives using ${framework.toUpperCase()}`);
console.log(`Top 3:`);
ranked.slice(0, 3).forEach((item) => {
  console.log(`  ${item.rank}. ${item.name} (score: ${item.score})`);
});
console.log(`Full results written to ${outputFile}`);