|
@@ -117,6 +117,10 @@ async function _render(planetName: string, pop: Pop) {
|
|
|
else if (price.exchange_code === 'UNIVERSE' && prices.get(price.ticker) === 0) // UNIVERSE always comes after IC1
|
|
else if (price.exchange_code === 'UNIVERSE' && prices.get(price.ticker) === 0) // UNIVERSE always comes after IC1
|
|
|
prices.set(price.ticker, price.vwap_30d);
|
|
prices.set(price.ticker, price.vwap_30d);
|
|
|
|
|
|
|
|
|
|
+ const results = paretoFront(pop, totalNeeds, siteCount, currentPOPIFilled, currentPop, lowerPop, totalPop, prices);
|
|
|
|
|
+ const maxPop = Math.max(...results.map((result) => result.change));
|
|
|
|
|
+ const bestUnitCost = Math.min(...results.filter((result) => result.change > 0).map((result) => result.cost / result.change));
|
|
|
|
|
+
|
|
|
renderTarget.innerHTML = `last POPR: ${lastPOPR.ReportTimestamp} (${lastPOPRts})
|
|
renderTarget.innerHTML = `last POPR: ${lastPOPR.ReportTimestamp} (${lastPOPRts})
|
|
|
<br>next POPR: ${new Date(nextPOPRts * 1000).toISOString()} (${nextPOPRts})
|
|
<br>next POPR: ${new Date(nextPOPRts * 1000).toISOString()} (${nextPOPRts})
|
|
|
|
|
|
|
@@ -202,9 +206,9 @@ async function _render(planetName: string, pop: Pop) {
|
|
|
<th>cost/day</th>
|
|
<th>cost/day</th>
|
|
|
<th>unit cost</th>
|
|
<th>unit cost</th>
|
|
|
</tr>
|
|
</tr>
|
|
|
- ${paretoFront(pop, totalNeeds, siteCount, currentPOPIFilled, currentPop, lowerPop, totalPop, prices).map((result) => {
|
|
|
|
|
|
|
+ ${results.map((result) => {
|
|
|
let unitCost = '';
|
|
let unitCost = '';
|
|
|
- if (result.cost > 0)
|
|
|
|
|
|
|
+ if (result.change !== 0)
|
|
|
unitCost = formatNum(result.cost / result.change);
|
|
unitCost = formatNum(result.cost / result.change);
|
|
|
return `<tr>
|
|
return `<tr>
|
|
|
<td>
|
|
<td>
|
|
@@ -218,9 +222,11 @@ async function _render(planetName: string, pop: Pop) {
|
|
|
${result.govProgram !== null ? `<br>${result.govProgram}` : ''}
|
|
${result.govProgram !== null ? `<br>${result.govProgram}` : ''}
|
|
|
</td>
|
|
</td>
|
|
|
<td>${formatPct(result.happiness)}</td>
|
|
<td>${formatPct(result.happiness)}</td>
|
|
|
- <td>${formatDelta(result.change)}</td>
|
|
|
|
|
|
|
+ <td style="color: ${color(result.change, 0, maxPop)}">${formatNum(result.change)}</td>
|
|
|
<td>${formatNum(result.cost)}</td>
|
|
<td>${formatNum(result.cost)}</td>
|
|
|
- <td>${unitCost}</td>
|
|
|
|
|
|
|
+ <td style="color: ${result.change > 0 ? color(bestUnitCost - result.cost / result.change, -bestUnitCost, 0) : '#777'}">
|
|
|
|
|
+ ${unitCost}
|
|
|
|
|
+ </td>
|
|
|
</tr>`;
|
|
</tr>`;
|
|
|
}).join('')}
|
|
}).join('')}
|
|
|
</table>
|
|
</table>
|
|
@@ -239,6 +245,13 @@ function formatDelta(n: number, withPlus: boolean = true): string {
|
|
|
return n.toString();
|
|
return n.toString();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function color(n: number, low: number, high: number): string {
|
|
|
|
|
+ // scale n from low..high to 0..1 clamped
|
|
|
|
|
+ const scale = Math.min(Math.max((n - low) / (high - low), 0), 1);
|
|
|
|
|
+ console.log(n, low, high, scale)
|
|
|
|
|
+ return `color-mix(in xyz, #0aa ${scale * 100}%, #f80)`;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function unemployed(people: number, change: number, openJobs: number, unemploymentRate: number): string {
|
|
function unemployed(people: number, change: number, openJobs: number, unemploymentRate: number): string {
|
|
|
let nextUnemployed;
|
|
let nextUnemployed;
|
|
|
if (openJobs <= people + change) {
|
|
if (openJobs <= people + change) {
|
|
@@ -332,6 +345,8 @@ function paretoFront(pop: Pop, totalNeeds: Record<Need, number>, siteCount: numb
|
|
|
results.push({config, govProgram, happiness, change, cost});
|
|
results.push({config, govProgram, happiness, change, cost});
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ results.sort((a, b) => b.change - a.change);
|
|
|
return results;
|
|
return results;
|
|
|
}
|
|
}
|
|
|
|
|
|