Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added autowheelbarrow script for stockpile management #1328

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

unboundlopez
Copy link

This script automatically manages wheelbarrows in Dwarf Fortress by adjusting their assignment to stockpiles based on what items the stockpile contains. Here's how it works:

Unassign Wheelbarrows: It checks all wheelbarrows in the game. If a wheelbarrow is assigned to a stockpile but not in use, it unassigns it.

Skip Certain Stockpiles: The script skips stockpiles that have specific items, like food, animals, furniture, weapons, etc. It also skips stockpiles that don’t contain anything at all.

Reassign Wheelbarrows: If the stockpile isn't skipped, the script assigns the right number of wheelbarrows to it based on the stockpile’s current state.

Automatic Checks: The script runs automatically every in-game day and rechecks wheelbarrows based on what dwarves are doing (specifically if they’re moving items into stockpiles).

Runs When Needed: The script starts automatically when the fortress map is loaded and stops when the map is unloaded.

This simplifies the management of wheelbarrows, ensuring they are assigned where needed and skipped for irrelevant stockpiles.

@unboundlopez
Copy link
Author

pre-commit.ci autofix

@unboundlopez
Copy link
Author

pre-commit.ci autofix

@unboundlopez
Copy link
Author

problem. If to many wheelbarrows there won't be enough time for the unit finish storing the wheelbarrow causing a never ending cycle of storing wheelbarrows.

Changed the amount of days it takes to loop the program as when there are to many wheelbarrows it can take a few days to carry the wheelbarrow over to a stockpile resulting in a infinite loop. 

Changing to 7 days results in even kids reaching their destination with a wheelbarrow before the 7th day.
Copy link
Member

@chdoc chdoc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs substantial improvements before it can be considered for inclusion. In particular, all the magic numbers need to be removed. Further, I'm not convinced that the negative approach (i.e. skipping stockpiles that have certain goods enabled) is the right one. It sounds more reasonable to me, to (only) assign wheelbarrows to stockpiles that have goods that use them.

Comment on lines +31 to +40
if stockpile_settings and stockpile_settings.food then
local food_settings = stockpile_settings.food
skip_wheelbarrows = #food_settings.meat > 0 or #food_settings.fish > 0 or #food_settings.unprepared_fish > 0 or
#food_settings.egg > 0 or #food_settings.plants > 0 or #food_settings.drink_plant > 0 or
#food_settings.drink_animal > 0 or #food_settings.cheese_animal > 0 or #food_settings.cheese_plant > 0 or
#food_settings.seeds > 0 or #food_settings.leaves > 0 or #food_settings.powder_plant > 0 or
#food_settings.powder_creature > 0 or #food_settings.glob > 0 or #food_settings.glob_paste > 0 or
#food_settings.glob_pressed > 0 or #food_settings.liquid_plant > 0 or #food_settings.liquid_animal > 0 or
#food_settings.liquid_misc > 0
--dfhack.println("Skipping stockpile due to food items" .. tostring(building))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea why stockpiles containing food should not have wheelbarrows. After all, it is quite possible to have a stockpile that has both food and boulders enabled.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand what you're saying, but I don't like it. The main purpose of creating this program was to maintain high efficiency in stockpiles containing wheelbarrows and, of course, have wheelbarrows move freely between stockpiles. Having food and stones together sounds insane to me, and this program will instead be a detriment to users, as those stockpiles will not be set to 1 automatically if stone is found or to x (wheelbarrows in the fort) per the program's functionality. Additionally, it will be a problem if 30 wheelbarrows inhabit a food and stone stockpile, as that is a possibility if I remove the conditions, which will cascade into a problem with food. I'm sorry for being stubborn; I think these conditions are essential. If this program is scrapped or repurposed, that's fine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you missed the main point of my remark. I wasn't suggesting that this script should fully account for mixed food and stone stockpiles - I agree that those aren't very useful.

My point was that the program logic could be drastically simplified using a positive check for item categories that require wheelbarrows (i.e. boulders). I don't know which other item categories consistently reach the 75Γ threshold for being carried using a wheelbarrow. Platinum and gold bars do, as do filled minecarts.

Another possibility might be to never check the item flags of the stockpile, and instead only modify the number of requested wheelbarrows for stockpiles that request at least one wheelbarrow. By default, that applies only to stone stockpiles, but the user could decide to assign in wheelbarrows to other stockpiles as well.

Comment on lines +61 to +76
-- Check if all flags are set to false (for the none stockpile)
if stockpile_settings and stockpile_settings.flags then
local flags = stockpile_settings.flags
if flags.animals == false and flags.food == false and flags.furniture == false and
flags.corpses == false and flags.refuse == false and flags.stone == false and flags.ammo == false and
flags.coins == false and flags.bars_blocks == false and flags.gems == false and flags.finished_goods == false and
flags.leather == false and flags.cloth == false and flags.wood == false and flags.weapons == false and
flags.armor == false and flags.sheet == false and
flags[17] == false and flags[18] == false and flags[19] == false and flags[20] == false and
flags[21] == false and flags[22] == false and flags[23] == false and flags[24] == false and
flags[25] == false and flags[26] == false and flags[27] == false and flags[28] == false and
flags[29] == false and flags[30] == false and flags[31] == false then
skip_wheelbarrows = true
--dfhack.println("Skipping NONE stockpile" .. tostring(building))
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking unnamed flags likely does not make sense at all. If anything, the code should probably check whether the stockpile has any goods enabled that make use of wheelbarrows, instead of checking whether it has (also) some goods enabled that do not make use of them.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You got me there. idk what they are for, but they exist so I added them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they only "exist" in the sense that the flag word has 32 bits. they don't have a (known) use and so predicating any behavior on their value will result in unpredictable behavior because their value is unpredictable

Comment on lines +109 to +120
-- Manage on state change
dfhack.onStateChange[GLOBAL_KEY] = function(sc)
if sc == SC_MAP_LOADED and df.global.gamemode == df.game_mode.DWARF then
event_loop() -- Start the event loop when the game map is loaded
elseif sc == SC_MAP_UNLOADED then
repeatutil.cancel(GLOBAL_KEY) -- Stop the event loop when the map is unloaded
end
end

-- Enable the script and start the event loop
enabled = true
event_loop()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have a look at how the enable API is supposed to be used:
https://docs.dfhack.org/en/stable/docs/dev/Lua%20API.html#script-enable-api

Comment on lines +89 to +107
local function auto_wheelbarrows()
for _, unit in ipairs(df.global.world.units.active) do
if dfhack.units.isCitizen(unit) and unit.job.current_job and unit.job.current_job.job_type == 38 then
unassign_wheelbarrows()
set_wheelbarrows_for_all_stockpiles()
return
end
end
end

-- Event loop function to call periodically
local function event_loop()
if enabled then
dfhack.println("Running autowheelbarrow.lua ")
auto_wheelbarrows()
-- Check again in 7 days
repeatutil.scheduleUnlessAlreadyScheduled(GLOBAL_KEY, 7, "days", event_loop)
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the rationale for your timing. It looks to me as if you check every 7 days whether there is, at that very moment, a unit that has the StoreItemInStockpile job, and only call auto_wheelbarrows if that is the case.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for the timing of seven days is children decide to pick up a wheelbarrow and it can take them quite a bit of time to haul the wheelbarrow meaning if the child is hauling for a while this will cause the program to run again and unassign all of the wheelbarrows in the fort resulting in a loop causing less work to be done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 7 days may be fine, even though it may be preferable to make this configurable. My question was more: why should the reassignment only occur if there is an active StoreItemInStockpile job.

:summary: Automatically manage wheelbarrows in stockpiles based on conditions.
:tags: fort auto stockpiles

This script automates the assignment of wheelbarrows to stockpiles based on specific criteria, such as the type of items stored in the stockpile. Stockpiles with food, armor, or all flags set to ``false`` will not have wheelbarrows assigned.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That documentation should clearly identify the problem the script is trying to solve. In particular, it should answer the following questions:

  • What is the deficiency of the automatic wheelbarrow assignment of DF that the script is addressing?
  • How is the script overcoming the aforementioned deficiency?

Comment on lines +81 to +82
local count = unassign_wheelbarrows()
building.max_wheelbarrows = count
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't calling unassign_wheelbarrows inside the loop mean that only the last valid stockpile will ever get wheelbarrows assigned to it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you're right I should unassign all wheelbarrows before the loop.

@unboundlopez
Copy link
Author

Thanks you are right. I like that you took the time to review this. Once I get home I’ll implementa few changes.

@unboundlopez
Copy link
Author

I forgot how to make changes in GitHub. So far, I want to make a single change, but there are likely other adjustments needed. I also want to add that I created this program and added it to the pull request because others may benefit as much as I do. The ability not to worry about the number of wheelbarrows assigned to individual stockpiles—since the program dynamically manages that—is a huge advantage. However, it can cause conflicts if the program also removes wheelbarrows from mixed stockpiles, such as food and stone, which I don't use in my world. If someone wishes to delete this pull request, take it over, or leave it in limbo, that's 100% fine with me.

@chdoc
Copy link
Member

chdoc commented Nov 2, 2024

I forgot how to make changes in GitHub.

This part is easy: just push additional commits to the branch from which you created this PR.

If someone wishes to delete this pull request, take it over, or leave it in limbo, that's 100% fine with me.

Don't get defensive. The purpose of code review is to discuss simplifications and improvements. Except for trivial PRs, changes are almost always necessary, even for experienced developers. If you're not up for this, feel free to just close this pull-request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants