Skip to content

Commit

Permalink
Add rate slider and scoring for public-owned scenarios; closes #79
Browse files Browse the repository at this point in the history
  • Loading branch information
toddmedema committed Apr 23, 2024
1 parent 9966d23 commit f19234e
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/Constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const TICK_MS = {
PAUSED: 250, // pause doesn't actually simulate frames, this is just for setTimeout timers
SLOW: 200,
NORMAL: 60,
FAST: 15,
FAST: 10,
};

export const INFLATION = 0.03;
Expand Down
2 changes: 2 additions & 0 deletions src/Types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ export interface ScenarioType {
tutorialSteps?: TutorialStepType[];
startingYear: number;
cash: number;
dollarsPerkWh: number;
durationMonths: number;
endTitle?: string;
endMessage?: string;
Expand All @@ -254,6 +255,7 @@ export interface GameType {
speed: SpeedType;
inGame: boolean;
feePerKgCO2e: number;
dollarsPerkWh: number;
monthlyMarketingSpend: number;
tutorialStep: number;
date: DateType;
Expand Down
33 changes: 31 additions & 2 deletions src/components/views/Finances.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,38 @@ export default class Finances extends React.Component<Props, State> {
}
/>
)}
{scenario.ownership === "Investor" && (
<div className="flex-newline"></div>
{scenario.ownership === "Public" && (
<Typography
className="flex-newline"
variant="body2"
color="textSecondary"
>
Electricity Rate:&nbsp;
<Typography color="primary" component="strong">
{formatMoneyConcise(game.dollarsPerkWh)}
</Typography>
/kWh
</Typography>
)}
{scenario.ownership === "Public" && (
<Slider
id="rateSlider"
value={game.dollarsPerkWh}
aria-labelledby="The rate you charge for electricity generation"
valueLabelDisplay="off"
min={0}
step={0.01}
max={0.3}
onChange={(e: any, newTick: number | number[]) =>
onDelta({
dollarsPerkWh: Array.isArray(newTick)
? newTick[0]
: newTick,
})
}
/>
)}
<div className="flex-newline"></div>
<Typography variant="h6" style={{ flexGrow: 0 }}>
Plotting{" "}
</Typography>
Expand Down
3 changes: 1 addition & 2 deletions src/components/views/Forecasts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ export default class Forecasts extends React.Component<Props, State> {
now.customers,
TICKS_PER_YEAR * years
);
// TODO performance optimization: figure out some way to just check if there's at least one storage facility built by the end of the timeline
// (right now we only track how much energy is stored, so can't just check end of timeline for that since batteries may be empty at that moment)

let hasStorage = false;
for (let i = 0; i < forecastedTimeline.length; i++) {
if (forecastedTimeline[i].storedWh > 0) {
Expand Down
3 changes: 2 additions & 1 deletion src/components/views/NewGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ function ScenarioListItem(props: ScenarioListItemProps): JSX.Element {
{s.summary}
<br />
<i>
{location.name} in {s.startingYear}
{location.name}, {s.startingYear}-
{s.startingYear + s.durationMonths / 12}
</i>
</span>
);
Expand Down
20 changes: 12 additions & 8 deletions src/components/views/NewGameDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,18 +211,22 @@ export default class NewGameDetails extends React.Component<Props, State> {
{/* Scoring algorithm should also be updated in Game.tsx */}
{scenario.ownership === "Investor" && (
<div>
<p>+40 pts for each $1B of net worth at the end</p>
<p>+2 pts for every 100k customers at the end</p>
<p>+1 pt for each TWh of electricity supplied</p>
<p>-2 pts for each gigaton of greenhouse gas emissions</p>
<p>-8 pts for each TWh of blackouts</p>
<p>+40 pts per $1B of net worth at the end</p>
<p>+2 pts per 100k customers at the end</p>
<p>+1 pt per TWh of electricity supplied</p>
<p>-2 pts per gigaton of greenhouse gas emissions</p>
<p>-8 pts per TWh of blackouts</p>
</div>
)}
{scenario.ownership === "Public" && (
<div>
<p>+10 pts for each TWh of electricity supplied</p>
<p>-5 pts for each gigaton of greenhouse gas emissions</p>
<p>-10 pts for each TWh of blackouts</p>
<p>
+/-80 pts per lifetime average $0.01/kWh charged above/below $
{scenario.dollarsPerkWh}/kWh
</p>
<p>+10 pts per TWh of electricity supplied</p>
<p>-5 pts per gigaton of greenhouse gas emissions</p>
<p>-10 pts per TWh of blackouts</p>
</div>
)}
</DialogContent>
Expand Down
3 changes: 2 additions & 1 deletion src/data/Facilities.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,8 @@ export function GENERATORS(
// https://energyinformative.org/lifespan-solar-panels/
},
// {
// // NOPE - very limited location options for these, and only two in the world are >20MW
// // as of 2018 very limited location options for these, and only two in the world are >20MW
// // TODO revisit, a lot's changed
// name: 'Tidal',
// fuel: 'Tides',
// description: 'Stable output except 4 times per day',
Expand Down
14 changes: 13 additions & 1 deletion src/data/Scenarios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const SCENARIOS = [
startingYear: 2019,
cash: 200000000,
feePerKgCO2e: 0,
dollarsPerkWh: 0.07,
durationMonths: 1,
endTitle: "Tutorial complete!",
endMessage: "Just a few tutorials to go",
Expand Down Expand Up @@ -97,6 +98,7 @@ export const SCENARIOS = [
startingYear: 2019,
cash: 200000000,
feePerKgCO2e: 0,
dollarsPerkWh: 0.07,
durationMonths: 12,
endTitle: "Tutorial complete!",
endMessage:
Expand Down Expand Up @@ -185,6 +187,7 @@ export const SCENARIOS = [
startingYear: 2019,
cash: 200000000,
feePerKgCO2e: 0,
dollarsPerkWh: 0.07,
durationMonths: 6,
endTitle: "Tutorial complete!",
endMessage:
Expand Down Expand Up @@ -269,6 +272,7 @@ export const SCENARIOS = [
startingYear: 2019,
cash: 200000000,
feePerKgCO2e: 0,
dollarsPerkWh: 0.07,
durationMonths: 1,
endTitle: "Tutorial complete!",
endMessage:
Expand Down Expand Up @@ -329,6 +333,7 @@ export const SCENARIOS = [
startingYear: 2019,
cash: 200000000,
feePerKgCO2e: 0,
dollarsPerkWh: 0.07,
durationMonths: 12,
endTitle: "Tutorial complete!",
endMessage:
Expand Down Expand Up @@ -391,6 +396,7 @@ export const SCENARIOS = [
startingYear: 2020,
cash: 200000000,
feePerKgCO2e: 0,
dollarsPerkWh: 0.07,
durationMonths: 12,
endTitle: "Tutorial complete!",
endMessage: `That's all we can teach you - the rest you'll have to learn by doing!`,
Expand Down Expand Up @@ -461,6 +467,7 @@ export const SCENARIOS = [
startingYear: 2020,
cash: 300000000,
feePerKgCO2e: 50 / 1000,
dollarsPerkWh: 0.07,
durationMonths: 12 * 12,
facilities: [
{ fuel: "Coal", peakW: 300000000 },
Expand All @@ -477,6 +484,7 @@ export const SCENARIOS = [
startingYear: 2006,
cash: 200000000,
feePerKgCO2e: 0,
dollarsPerkWh: 0.07,
durationMonths: 12 * 20,
facilities: [{ fuel: "Coal", peakW: 500000000 }],
},
Expand All @@ -490,6 +498,7 @@ export const SCENARIOS = [
startingYear: 2004,
cash: 250000000,
feePerKgCO2e: 0,
dollarsPerkWh: 0.07,
durationMonths: 12 * 12,
facilities: [
{ fuel: "Oil", peakW: 450000000 },
Expand All @@ -507,6 +516,7 @@ export const SCENARIOS = [
startingYear: 2002,
cash: 200000000,
feePerKgCO2e: 0,
dollarsPerkWh: 0.07,
durationMonths: 12 * 12,
facilities: [
{ fuel: "Oil", peakW: 100000000 },
Expand All @@ -523,9 +533,10 @@ export const SCENARIOS = [
startingYear: 2000,
cash: 200000000,
feePerKgCO2e: 0,
dollarsPerkWh: 0.07,
durationMonths: 12 * 20,
facilities: [
{ fuel: "Oil", peakW: 200000000 },
{ fuel: "Oil", peakW: 220000000 },
{ fuel: "Natural Gas", peakW: 200000000 },
{ fuel: "Coal", peakW: 100000000 },
],
Expand All @@ -540,6 +551,7 @@ export const SCENARIOS = [
startingYear: 1980,
cash: 180000000,
feePerKgCO2e: 0,
dollarsPerkWh: 0.07,
durationMonths: 12 * 20,
facilities: [
{ fuel: "Coal", peakW: 200000000 },
Expand Down
6 changes: 2 additions & 4 deletions src/data/Weather.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const DUMMY_WEATHER = {
WIND_KPH: 10,
};

// TODO download weather for all locations at start with a 2s init delay, like loading audio (but after audio)
// TODO download weather for all locations at start with a 2s init delay, like loading audio (but after audio) for offline play
// But only if worker: true starts working - TICKET: https://github.com/mholt/PapaParse/issues/753
// Ideally caching this... so maybe upgrade to use https://tanstack.com/query/latest/docs/framework/react/overview ?
export function initWeather(location: string, callback?: any) {
Expand Down Expand Up @@ -79,9 +79,7 @@ export function getWeather(date: DateType): RawWeatherType {
};
}

// Is later multiplied by cloudiness
// TODO should this just take in cloudiness? The game knows future weather...
// TODO change to watts per sq meter or some fixed value, and verify that it's returning reasonably accurate values per location and season
// TODO verify that it's returning reasonably accurate values per location and season
// (hoping that day length alone is a sufficient proxy / ideally don't need to make it any more complex)
// https://earthobservatory.nasa.gov/features/EnergyBalance/page2.php
// indicates a roughly linear correlation that each degree off from 0*N/S = 0.7% less sunlight
Expand Down
26 changes: 19 additions & 7 deletions src/reducers/Game.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const initialGame: GameType = {
speed: "PAUSED",
inGame: false,
feePerKgCO2e: 0, // Start on easy mode
dollarsPerkWh: 0.07,
monthlyMarketingSpend: 0,
tutorialStep: -1, // Not set to 0 until after card transition, so that the target element exists
facilities: [] as FacilityOperatingType[],
Expand Down Expand Up @@ -402,6 +403,12 @@ function tickState(state: GameType) {
blackouts: Math.round(-8 * blackoutsTWh),
}
: {
rate: Math.round(
80 *
100 *
(scenario.dollarsPerkWh -
summary.revenue / (summary.supplyWh / 1000))
),
supply: Math.round((10 * summary.supplyWh) / 1000000000000),
emissions: Math.round((-5 * summary.kgco2e) / 1000000000),
blackouts: Math.round(-10 * blackoutsTWh),
Expand Down Expand Up @@ -441,17 +448,24 @@ function tickState(state: GameType) {
message: scenario.endMessage || (
<div>
Your final score is {finalScore}:<br />
<br />+{score.supply} pts from electricity supplied
<br />
{score.supply} pts from electricity supplied
<br />
{scenario.ownership === "Investor" && (
<span>
+{score.netWorth} pts from final net worth
{score.netWorth} pts from final net worth
<br />
</span>
)}
{scenario.ownership === "Investor" && (
<span>
+{score.customers} pts from final customers
{score.customers} pts from final customers
<br />
</span>
)}
{scenario.ownership === "Public" && (
<span>
{score.rate} pts from electric rates
<br />
</span>
)}
Expand Down Expand Up @@ -684,13 +698,11 @@ function updateSupplyFacilitiesFinances(
now.storedWh = storedWh;

// Update finances
// TODO actually calculate market price / sale value
// Alternative: use rate by location, based on historic prices (not as fulfilling) - or at least use to double check
const dollarsPerWh = 0.07 / 1000;
// TODO have starting dollarsPerkWh rate by location, based on historic prices (not as fulfilling) - or at least use to double check
const supplyWh =
(Math.min(now.supplyW, now.demandW) / TICKS_PER_HOUR) * GAME_TO_REAL_YEARS; // Output-dependent #'s converted to real months, since we don't simulate every day
const demandWh = (now.demandW / TICKS_PER_HOUR) * GAME_TO_REAL_YEARS; // Output-dependent #'s converted to real months, since we don't simulate every day
const revenue = supplyWh * dollarsPerWh;
const revenue = (supplyWh / 1000) * state.dollarsPerkWh;

// Facilities expenses
let kgco2e = 0;
Expand Down

0 comments on commit f19234e

Please sign in to comment.