recipes.js 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. // src/recipes.ts
  2. async function fetchData() {
  3. const [buildingsResponse, recipesResponse, exchangesResponse] = await Promise.all([
  4. fetch("https://api.prunplanner.org/data/buildings"),
  5. fetch("https://api.prunplanner.org/data/recipes"),
  6. fetch("https://api.prunplanner.org/data/exchanges")
  7. ]);
  8. const [buildings, recipes, prices] = await Promise.all([
  9. buildingsResponse.json(),
  10. recipesResponse.json(),
  11. exchangesResponse.json()
  12. ]);
  13. return { buildings, recipes, prices };
  14. }
  15. function render({ buildings, recipes, prices }) {
  16. const priceMap = new Map;
  17. for (const price of prices)
  18. if (price.ExchangeCode == "PP7D_IC1")
  19. priceMap.set(price.MaterialTicker, price);
  20. const buildingMap = new Map;
  21. for (const building of buildings) {
  22. const cost = building.BuildingCosts.reduce((sum, mat) => {
  23. const price = priceMap.get(mat.CommodityTicker);
  24. if (price === undefined)
  25. throw new Error(`missing price for ${mat.CommodityTicker}`);
  26. return sum + price.PriceAverage * mat.Amount;
  27. }, 0);
  28. buildingMap.set(building.Ticker, cost);
  29. }
  30. const profits = [];
  31. for (const recipe of recipes) {
  32. const runsPerDay = 1000 * 60 * 60 * 24 / recipe.TimeMs;
  33. if (recipe.Outputs.length !== 1) {
  34. console.warn(`${recipe.RecipeName} doesn't have 1 output`);
  35. continue;
  36. }
  37. const output = recipe.Outputs[0];
  38. const outputPrice = priceMap.get(output.Ticker);
  39. if (outputPrice === undefined)
  40. throw new Error(`missing price for ${output.Ticker}`);
  41. const revenuePerRun = output.Amount * outputPrice.PriceAverage;
  42. const costPerRun = recipe.Inputs.reduce((sum, input) => {
  43. const price = priceMap.get(input.Ticker);
  44. if (price === undefined)
  45. throw new Error(`missing price for ${input.Ticker}`);
  46. return sum + price.PriceAverage * input.Amount;
  47. }, 0);
  48. const dailyProfit = (revenuePerRun - costPerRun) * runsPerDay;
  49. const buildingCost = buildingMap.get(recipe.BuildingTicker);
  50. if (buildingCost === undefined)
  51. throw new Error(`missing building cost for ${recipe.BuildingTicker}`);
  52. if (outputPrice.Traded === 0 || buildingCost > 1200000)
  53. continue;
  54. profits.push({
  55. recipeName: recipe.RecipeName,
  56. dailyProfit,
  57. traded: outputPrice.Traded,
  58. priceAverage: outputPrice.PriceAverage,
  59. buildingCost
  60. });
  61. }
  62. profits.sort((a, b) => b.dailyProfit - a.dailyProfit);
  63. const fmt = new Intl.NumberFormat(undefined, { maximumFractionDigits: 2 });
  64. const tbody = document.querySelector("tbody");
  65. tbody.innerHTML = "";
  66. for (const profit of profits) {
  67. const row = document.createElement("tr");
  68. row.innerHTML = `
  69. <td>${profit.recipeName}</td>
  70. <td>${fmt.format(profit.dailyProfit)}</td>
  71. <td>${fmt.format(profit.traded * profit.priceAverage)}</td>
  72. <td>${fmt.format(profit.traded)}</td>
  73. <td>${fmt.format(profit.buildingCost)}</td>
  74. `;
  75. tbody.appendChild(row);
  76. }
  77. }
  78. fetchData().then(render);
  79. //# debugId=1CA72A12BC1F1FDE64756E2164756E21