frnsys / half_earth

Half-Earth Socialism: The Game, for Half-Earth Socialism (Verso 2022)
GNU Affero General Public License v3.0
39 stars 7 forks source link

New game starts with a critical electricity shortage, fossil fuels produce zero power. #269

Closed sterlind closed 3 months ago

sterlind commented 9 months ago

Day 1 starts with a critical electricity shortage. Coal shows zero energy production and zero pollution. Petroleum later stops working for no discernible reason.

Also, 93% of energy usage is due to "chemical." It's not clear to me what "chemical" is or how to fix it.

I could have sworn that just a few days ago when I discovered this game, there weren't these issues. It becomes nearly impossible to progress past term 1 due to anger over critical shortages tanking public opinion.

sterlind commented 9 months ago

Yep, definitely a regression. Commits prior to 0d448ca29e9cc8fba380d47fc8aaa657bd7a8eed show Coal Power Generation producing 9 electricity and 9 pollution, while commits after show zero for both.

sterlind commented 9 months ago

Actually, even as far back as 0d448ca, the bug seems to manifest in some energy: image For example, this shows 0/0 (despite accounting for 25%), but coal works: image

sterlind commented 9 months ago

Actually, I think the problems even begin with 2eaa5575eda4558f6283f73ea833606643fabcc2. At this commit, the game still starts with a production shortage of electricity, and natural gas is 0/0. However, going back to the commit prior to that (15f3f51ed94b9e08af4ae0f0b7fcb1712c657972) fixes electricity stats:

image

you can see here it's 6/3 instead of 0/0 like above.

frnsys commented 9 months ago

Thanks for letting me know and for figuring out where things changed. I'll try to sort this out soon.

sterlind commented 9 months ago

@frnsys so yesterday I did some debugging. Commit 2eaa5575eda4558f6283f73ea833606643fabcc2 did introduce bad coefficients in content.rs, but it really just exposed another bug, which causes processes to silently stop producing with no apparent cause. I will explain and propose a fix.


Here's my high-level understanding of how simulation steps work:

On each simulation step, Game::simulate() -> State::step_production() -> State::update_production() -> State::update_demand() is called, which:

    pub fn update_demand(&mut self) {
        let (output_demand, resources_demand) = self.calculate_demand();
        self.output_demand = output_demand;
        self.resources_demand = resources_demand;
  1. Calculates demand for process outputs (animal/plant calories, fuel, electricity) and natural resources (water, land) via calculate_demand()
  2. Generates a ProductionOrder for each process, to produce the demanded resources based on each process's mix share:
        let orders: Vec<ProductionOrder> = self.processes.iter()
            .map(|p| p.production_order(&self.output_demand)).collect();

This is done in two passes, with the second pass factoring in additional fuel/electricity demanded by other processes, and values are tweaked based on game state and flags, but the details aren't important.

Once demand is calculated, production is calculated:

        // Run production function
        let (produced_by_process,
             produced_by_type,
             consumed_resources,
             consumed_feedstocks,
             byproducts) = produce(&orders, &self.resources, &self.feedstocks);
  1. Scores are calculated for each production order, based on how much of the shared resources and feedstock they use.
        let r = process.adj_resources();
        let resource_score: f32 = r.items().iter().map(|(k, v)| *v/(resources[*k] + 1.)).sum();
        resource_scores.push(resource_score);

        let f = process.adj_feedstock_amount();
        let feedstock_score = f/(feedstocks[process.feedstock.0] + 1.);
        feedstock_scores.push(feedstock_score);

        scores.insert(*i, (resource_score, feedstock_score));

Production orders are sorted by these scores. Heavy processes that use the most resources come first:

    // Sort so that the best (lowest) scoring are at the end
    indices.sort_by_cached_key(|i| {
        let (r, f) = scores.get(i).unwrap();
        let resource_score = r/max_resource_score;
        let feedstock_score = f/max_feedstock_score;
        ...
        let score = feedstock_score + resource_score + byproduct_score;

        // Hacky
        -((score * 100000.).round() as isize)
    });
  1. The highest-scoring order is processed, deducting that process's use of resources from available_resources, along with feedstock usage:
            let amount = produce_amount(
                &orders[order_idx],
                &mut resources,
                &mut feedstocks,
                &mut produced_byproducts);
            produced[order_idx] = amount;
  1. The process is removed from the queue, and we repeat, sorting the remaining processes and picking the next process that consumes most of the remaining resources.

So to recap, this is a greedy algorithm which selects the process with the highest resource utilization, and applies it. However, it makes terrible decisions handling resource shortages! For example, consider a run with a land shortage, Industrial Livestock Ag and Nuclear Power. Livestock is applied first, since it wants to use >100% of the available land. Now there's zero land left over, so Nuclear Power fails to satisfy any of its production order, since it requires a tiny amount of land. In fact, every process requiring any land will now fail, since cows are on every scrap of earth that hasn't been turned into a national park!

To fix this, I propose inverting the sort order, to put the resource hogs last. ...There's also the optimal solution, which is to use Linear Programming:

Constants:

Variables:

The amount of resource $r$ consumed is $\sump [A{rp} xp]$. The gross amount of resource $r$ produced is $\sum{p \in P(r)} xp$. Therefore, the net amount of resource $r$ produced is $\sum{p \in P(r)} [x_p] - \sumq [A{rq} x_q]$. So the objective is to minimize how much net production falls short of resource demands, i.e.

min $\sum_r [D_r - f_r]$, such that:

  • $0 \le x_p \le M_p D_r$, for $p \in P(r)$ (process $p$ is non-negative and can't exceed its mix ratio.)
  • $\sump A{rp} x_p \le T_r$, if $r$ is an input resource.
  • $fr = \sum{p \in P(r)} [x_p - \sumq [A{rq} x_q]]$, if $r$ is an output resource (define net production.)
  • $0 \le f_r (\le D_r)$, if $r$ is an output resource (can't consume more fuel, electricity than produced. Guaranteed to be bounded by $D_r$ due to the mix ratio constraint, but I restated for clarity.)

Note that we don't differentiate between feedstocks and natural resources - both are input resources, feedstocks just have no intrinsic demand. Also note that intermediate consumption of resources (e.g. farms requiring electricity) is handled here, so it should not be added to the demands.

I hope this doesn't look like a lot of math - LPs are actually pretty easy to write. But it's probably more work than flipping a sign (if that works!)


Also, there's an additional UI bug where only resources with limits defined (e.g. vertical farms, hydropower) will show red bars if production shortfalls occur. That's why the 0/0 on nuclear was so mysterious to me. It'd also be nice to highlight which resource fell short. But I didn't have time to look into fixing that.

ferninabsurdism commented 9 months ago

I am also seeing some odd behavior with the game in line with this ticket.

  1. Game starts with a biodiversity event and offers me "The Ark" on start. Immediate production shortages on start.
  2. Emissions line is no longer visible. Land use is.
  3. Only natural gas and nuclear cards are marked with any production values.
  4. Emissions skyrocket with available options with no way to keep them down.
  5. Fuel sources will have a popup saying they can only count for 0% production. The card will not reflect that number.
frnsys commented 9 months ago

Amazing work, thank you so much for doing all this investigating! Looking back on the commit you highlighted it does look like somehow some of the values are using the wrong units, especially the Chemical and Concrete industries. I'm adjusting those to the correct scale and also flip the scoring ordering for now...if you have a moment please give it a try and let me know if it helps. I'll look more into the other issues you raised. We were originally going with a linear programming approach but ended up abandoning it, I can't recall why.

ferninabsurdism commented 9 months ago

I am still seeing some cards with zero values (vertical farming as an example). Is this intended behavior?

sterlind commented 9 months ago

@frnsys Unfortunately, I think something else regressed. After the first planning session, emissions rise significantly regardless of what you do. The Hero saves your first turn, but there's ultimately no recovering. Also, biofuels are now actually worse than petroleum on emissions, and even blue hydrogen has nearly max pollution. Maybe the numbers you're using for biofuels don't include the CO2 absorbed during production? I don't think the earlier version I played had this error.

Is there a last-known-good commit you could revert to, maybe? One where the game's known to be relatively playable? That way we can have a working version of the site while we look into a proper fix.

frnsys commented 9 months ago

I have some time today so I'm going to try and narrow down the config values that are causing these problems. I'll keep you updated

frnsys commented 9 months ago

I think these problems may be due to the re-worked biodiversity numbers. I suspect that it's easier to max out biodiversity pressure which is for some reason treated as a "resource" which is limiting process outputs. It's been awhile since I looked closely at the code so I need to confirm that this is the cause, but bumping up the max biodiversity pressure to something much higher seems to resolve the production problems.

frnsys commented 9 months ago

Doesn't seem to be biodiversity-related...just to check--@sterlind and @ferninabsurdism are you still seeing the issues in a new game?

frnsys commented 9 months ago

The issue with The Ark event was biodiversity related, I have an adjustment that will fix that ready. I'm having trouble re-producing the production-related problems and the only reason I can think of is that saved games use the parameters from when the game was saved, and not whatever the most recent ones are. If you still see the issues (after I update in just a few min) after a new game, let me know and I'll take another look.