Configurable mine resource behavior - #1942
DevOpsOfChaos wants to merge 28 commits into
Conversation
Flamefire
left a comment
There was a problem hiding this comment.
Thanks for taking care of that.
Please remove duplications and check back with what was discussed before. This includes:
- There is no need for multiple addons for a single mine type:
GRANITEMINES_WORK_EVERYWHEREis superflous and all the existing "inexhaustible*" addons should be integrated into this ones and use the same IDs: The OFF setting should be "Default" and ON (the new) "inexhaustible" FindPointWithResourceQuietduplicates code already present -> Reuse existing one, can be done by simply adding a default bool parameternotifyINEXHAUSTIBLE_MINESaddon should be removed: When detected during loading it should set the new addons accordingly, keep ID for that with a TODO for gamedata version increasing- Explain
CalcAverageDisplayProductivityplease, why the extra methods? - Isn't there already a method to convert AIResource to Resource so
GetMineBuildingTypedoesn't need to be (fully) implemented twice? CanCreateWorkEverywhereResourcelooks odd: "Work everywhere" should be just that: Ignore any resource in the ground, not add resources to the world, doesn't it? So just skip the searchIsMineResourceDepletableshouldn't useGetConfiguredMineResourceBehavior, in fact it seems only a single function is required, i.e. both Get... combined.
Maybe more.
Please give a quick summary of how "s4 like behavior" is supposed to work, so @Spikeone can verify it is what he intended in #1501 and we can verify it is implemented according to that spec.
Thanks, that direction makes sense. My understanding of the requested cleanup is:
My current S4-like behavior implementation is intended to work like this:
I’ll rework the implementation toward the existing-ID/list-setting approach and remove the duplicated/intermediate pieces instead of stacking this on top of the old addon set. |
@Spikeone Looks correct to me. Can you confirm? "possible resource capacity" isn't fully clear to me. There are 7 points considered and currently each resource gives 5% chance. I.e. when all have at least 3 resources we will have 100%. IIRC the max amount of resources per node is 7 (we should have a constant for that if we don't already have one) |
Good point. The current implementation intentionally models the existing S4-like “full productivity around ~20 remaining resources” behavior rather than deriving 100% from the absolute theoretical map capacity. The current
I agree the current formulation makes the I will rework this to express the behavior through named constants / derived calculation so the intended reference point becomes explicit instead of looking like a magic number. |
Yes, its 100% at (exactly) 20 resources, but why 20 and not 10, 30 or 40 when 49 is the theoretical maximum? Or even as low as 7 which would be 1 node with full resources. |
|
Updated the PR to address the requested architecture cleanup and the S4-like chance clarity point. Main changes:
S4-like behavior after the rework:
Validation:
|
Flamefire
left a comment
There was a problem hiding this comment.
Most things are now readability:
TODO(Replay)and/orTODO(Savegame)allows to quickly find all places- "display"productivity seems odd
- less code is usually better: Reduce C&P and avoid enum constraints where not necessary or document why
- The new
notifyparam forFindPointWithResourcenow looks redundant so notification might get moved out which is better for SRP as a "find" should not "notify"
…rable-mine-resource-behavior
…rable-mine-resource-behavior
|
Updated the branch against current master and addressed the remaining mine-resource review points. The follow-up now uses one productivity path for the expected primary-resource yield, removes the parallel display-productivity layer, and keeps fallback goods outside that primary-resource metric. It also fixes the S4-like resource calculation to consider all matching resource points within the mine radius, with regression coverage for multiple points, wrong resource types, out-of-range points, empty resources, and rounding. Validated locally:
|
|
Followed up on the remaining review-level cleanup points.
Validated locally:
|
|
There are a couple minor points left that need to be addressed so we can get this merged. Just in case they got missed |
- tests/testGamePlayer.cpp: move FindMinePosition into the anonymous namespace. Without this the build fails with -Werror=missing-declarations on GCC and Clang. - nofWellguy: report running out of resources explicitly. Moving the notification out of nofWorkman::FindPointWithResource only re-added it for mines, so wells stopped reporting "This well has dried out": no post message, no NoRessources BuildingNote, productivity was not reset and the DEMOLISH_BLD_WO_RES addon no longer triggered. Adds a regression test that fails without the fix. - GlobalGameSettings: apply the legacy INEXHAUSTIBLE_MINES migration to every mine type that is still at its default behavior. GRANITEMINE_RESOURCE_BEHAVIOR reuses the id of the old INEXHAUSTIBLE_GRANITEMINES bool addon, so old settings, savegames and replays always contain a value for that id and granite mines were silently left exhaustible even though the old global setting made them inexhaustible. Adds a regression test. - MaxEnumValue.h: drop the redundant inline on the constexpr isValidEnumValue, which clang-tidy rejects (readability-redundant-inline-specifier). - Apply clang-format 10 to the lines that exceeded the column limit after the addon rename (testAI.cpp, testProduction.cpp). The CI formatting job rejected them. - Remove leftovers: the now single-use CalcAverageProductivity helper taking a member function pointer, the unused nobUsual::GetProductivityPointer() (which would return the unscaled value) and the always-true depletable check in the S4-like production branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Upstream master replaced boost::optional by std::optional, so
helpers::OptionalEnum no longer accepts boost::none. Returning {} works
both before and after that change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Renaming INEXHAUSTIBLE_GRANITEMINES and dropping the global INEXHAUSTIBLE_MINES addon also changed the ADDON_* constants exported to Lua, which doc/lua/functions.md documents as public API. Existing map scripts using the old names would have silently stopped working. Add an explicit alias for the renamed addon (same id, same meaning of value 1) and route the removed global addon through the same migration that is used for old settings and savegames. The obsolete addon stays unregistered, so neither the addon UI nor serialization change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Final verification update for head In addition to addressing the remaining inline review points, the branch was tested through a temporary local integration with current Lua backward compatibility was also preserved. Locally verified with GCC 14 and Clang 18, Debug and warnings-as-errors: complete build, |
Flamefire
left a comment
There was a problem hiding this comment.
Ok, now mostly some changes to make the test more self-documenting and clean. A few things look odd there.
Also LuaInterfaceGameBase::GetFeatureLevel must be increased to cater for the new constants and this change should be documented too
…p to 7, cleanup magic numbers and test structure Co-Authored-By: Claude <noreply@anthropic.com>
|
Addressed all review points:
Locally verified with GCC 14 and Clang 18 as before — ctest run pending submodule init. Ready for re-review. |
- Split GraniteMineResourceBehaviorAffectsAIMineSearch into two tests because the AI caches GGS on construction; Inexhaustible needs its own test with post-selection construction. - Bump s4LikeComparisonGFs 2000→5000 so the resource-depletion cycle reliably completes within the window. - Fix Boost.Test iterator-printing error in testGamePlayer.cpp.
Flamefire
left a comment
There was a problem hiding this comment.
Sorry for the delay, vacation time and such...
I went over the changes and resolved adressed comments. A few are still open or new, but should be quick to resolve.
And it needs a clang-format run, see CI
|
No worries about the delay, hope the vacation was good. All the open points are in, plus the clang-format run. On the AI tests: combining them does work, it just needs a fresh AI per setting. One thing I left alone: the map size in One caveat: I could not build locally this time (the machine I am on has neither MSVC nor Boost set up), so the test run is on CI here. |
|
Small correction to my previous comment: I did get a local build working after all, so the caveat at the end of it is outdated. Built and ran on Linux with GCC 15.2 and Boost 1.90:
The combined AI test and the reworked out-of-range check in One thing you may want to know: the Actions runs for b066a8e are currently sitting in |
Formatting (clang-format 10, taken verbatim from the failing CI job): - testAI.cpp: drop stray blank line before closing brace - testProduction.cpp: reflow CreateMine cast, comment alignment in NoOutputFallbackCase, setSelection call and the BOOST_DATA_TEST_CASE_F dataset Review points: - Combine GraniteMineResourceBehaviorAffectsAIMineSearch and InexhaustibleGraniteDoesNotImplyWorkEverywhereForAI again. They were split because AIPlayerJH rates the nodes once on construction, so changing the addon afterwards had no effect. A lambda that builds its own AI per behavior keeps that constraint and still shows the difference between both settings in one place. - testGamePlayer.cpp: inline the WorldFixtureMineRadius1P alias (used once), shorten the map size comment - testGamePlayer.cpp: drop the CreateBuilding/building-quality note and say what the HQ offset is actually for - testGamePlayer.cpp: reword setCoalAmounts comment - testGamePlayer.cpp: make the out-of-range point out of range by construction instead of searching the radius for it - testProduction.cpp: index the inventory with GoodType::Coal directly - testSerialization.cpp: drop the misleading "(usually disabled)" Verified on Linux with GCC 15.2 and Boost 1.90: - ctest: 20/20 passed (incl. Test_UI, Test_lua, Test_drivers) - Test_integration: 215/215 test cases, 93537 assertions - Production 17/17, AI 10/10, Serialization 11/11 - clang-format clean on all four files, git diff --check clean Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
b066a8e to
64b4fc0
Compare
Flamefire
left a comment
There was a problem hiding this comment.
On the AI tests: combining them does work, it just needs a fresh AI per setting. AIPlayerJH rates the nodes once on construction, so flipping the addon afterwards had no effect and the assertion was silently testing nothing — that was the reason for the split. It is one test again now, with a small lambda that builds its own AI per behavior, so the WorkEverywhere/Inexhaustible difference sits side by side.
Great! Makes sense of course but isn't immediately clear. Can you put a comment with that finding there? E.g. AI assumes game settings do not change, so need to create it (a new instance) after the settings are changed
I left a previous comment there on also testing Inexhaustible. That is currently incomplete: It only tests that there are no places when inexhaustible is set on NO resources, but not that there are places if there are resources. That's a trivial extension to the test. So can you add that? Unless I missed that this is already tested somewhere else. But it surely does make sense to add the 3-4 lines there to highlight the differences.
One thing I left alone: the map size in MineProductivityFixture. I inlined the alias and shortened the comment, but kept the literal 20x12 — with MINER_RADIUS == 2 anything expressed in terms of it either gets too small for the HQ offset the test relies on, or needs fairly arbitrary multipliers. Happy to change it if you had a particular form in mind.
I would have imagined something like MINER_RADIUS * 2 + x. Otherwise you'd need an assertion that size.x/y > MINER_RADIUS * 2 (if that's what you meant) to ensure your assumption does not break.
It basically comes down to non-local assumptions: Someone changing MINER_RADIUS might not be aware of tests relying on it. See also the new comment below that.
One thing you may want to know: the Actions runs for b066a8e are currently sitting in action_required, so they need your approval before CI picks the commit up.
@Flow86 I think it makes sense to disable that requirement until we see the need for that by e.g. a flood of trash PRs. Currently it seems more like a nuisance.
| BOOST_TEST(findsMineSpotFor(MineResourceBehavior::WorkEverywhere, AIResource::Granite)); | ||
| BOOST_TEST(!findsMineSpotFor(MineResourceBehavior::WorkEverywhere, AIResource::Coal)); | ||
| // Inexhaustible does not imply "work everywhere": without an actual deposit there is still no spot. | ||
| BOOST_TEST(!findsMineSpotFor(MineResourceBehavior::Inexhaustible, AIResource::Granite)); |
There was a problem hiding this comment.
For reference with the above asserts maybe:
| BOOST_TEST(!findsMineSpotFor(MineResourceBehavior::Inexhaustible, AIResource::Granite)); | |
| BOOST_TEST(!findsMineSpotFor(MineResourceBehavior::Inexhaustible, AIResource::Granite)); | |
| BOOST_TEST(!findsMineSpotFor(MineResourceBehavior::Inexhaustible, AIResource::Coal)); |
And the opposite is missing: Have a single split with granite. Or is there another AI test for Inexhaustible granite with some granite somewhere?
If not I'd suggest to place granite somewhere , e.g. neighbor to searchCenter and test:
BOOST_TEST(findsMineSpotFor(MineResourceBehavior::Inexhaustible, AIResource::Granite));
BOOST_TEST(!findsMineSpotFor(MineResourceBehavior::WorkEverywhere, AIResource::Coal));
|
|
||
| MineProductivityFixture() | ||
| { | ||
| // Offset > MINER_RADIUS so no node of the mine radius is covered by the HQ and each of them can hold coal. |
There was a problem hiding this comment.
Why not use MINER_RADIUS here? This assumption (Is it 4 > MINER_RADIUS?) may break in the future
And I don't understand why this is a requirement at all: What does the HQ has to do with this and why wouldn't a node be able to hold coal? I.e. what is the difference between the nodes at hq + 4 and hq + 3?
Summary
This PR implements configurable mine resource behavior as a cleaned-up replacement path for the broader #1501 direction.
It adds per-mine resource behavior settings for coal, iron, gold, and granite mines, while reusing existing inexhaustible mine addon IDs where appropriate instead of stacking additional addons on top.
Details
INEXHAUSTIBLE_*addon IDs as per-mine behavior list settings where possibleONvalues to the newInexhaustiblebehaviorINEXHAUSTIBLE_MINESsettings to per-mine inexhaustible behavior when loading old settingsWork everywhereas ignoring ground-resource requirements instead of creating resources in the worldS4-like behavior
For S4-like behavior:
Work-everywhere behavior
Work everywheremeans that the mine skips the ground-resource requirement.It does not create temporary resources in the world and does not deplete such generated resources.
Validation
clang-format version 10.0.0Test_integrationTest_UITest_integration --run_test=ProductionTest_integration --run_test=AITest_integration --run_test=Serializationctest --test-dir .\build-vs-x64-debug-local -C Debug -R "^Test_integration$" --output-on-failureTest_UI.exe --report_level=shortgit diff --check upstream/master...HEADpassed