DFHack / dfhack

Memory hacking library for Dwarf Fortress and a set of tools that use it
Other
1.84k stars 461 forks source link

buildingplan: support all building types #1640

Closed myk002 closed 3 years ago

myk002 commented 3 years ago

The next big feature I have planned for quickfort is support for all buildings in buildingplan.

I have a few questions, though, and I'm not on IRC reliably enough to ask them there:

1) How do we match an item against a building construction job item filter?

In order to support all buildings, I'll need to support a wider variety of building items, including generic building materials. buildingplan goes through all free game items and assigns pertinent ones to buildingplan-managed buildings. This is easy to do for the furniture types buildingplan currently supports, but how can we do it generically for any filter returned from buildings.lua? Say the filter says "fire-safe building material", what is the best way to select a matching item in the game world?

Edit: I just discovered dfhack.job.isSuitableItem(). Let me investigate that. Edit2: that looks like it will work. Nice.

2) How do we assign just one of several items in a construction job?

This might become obvious once I start implementing, but just in case it's difficult, are there any special steps I have to take for assigning items to a suspended well construction job if not all items are available at the same time? Can I assign one item and keep the job suspended? are there any negative consequences of doing that?

I haven't fully designed the data structures and item selection algorithm yet, but my overall plan is this:

myk002 commented 3 years ago

btw, could you assign this bug to me? I plan to implement it myself, just posting as an issue for tracking purposes.

lethosor commented 3 years ago

Assigned + labeled, hopefully appropriately. Not sure on (2) - you might need to add a generic_ref or specific_ref to keep the item "attached" to a job, but that might be something you need to do even if the job isn't suspended. I haven't taken a close look at buildings/jobs in a while, but maybe someone else knows more.

myk002 commented 3 years ago

Not sure on (2) - you might need to add a generic_ref or specific_ref to keep the item "attached" to a job, but that might be something you need to do even if the job isn't suspended.

I think I got it figured out. I'm testing the final code now to make sure all the items end up in the correct order, regardless of the order they were attached in.

myk002 commented 3 years ago

I realize I forgot to attach my data structure and design notes:

Operations: Register building and its jobs Is bld registered? Get filter for bld Match world items to jobs, iterate through jobs

Data structures: unordered_map<bld , PlannedBuilding> for bld lookup, owns PlannedBuildings map<vector_id, map<filter_hash, (job_filter, ItemFilter, queue<pair<bld , job_item_index>>>)>> for job item matching, FIFO

Algorithm: addPlannedBuilding: for each filter, hash item filter + buildingplan filter, add to data structure. Create branches/mappings that don’t already exist.

Unregister: just remove from unordered_map. Can remove job references in main data structure lazily when we encounter them. This will avoid having to index all job locations in the structure or iterating through it to find the garbage.

doCycle: for each item vector that we have filters for, match each item against each filter tuple for that vector id that still has job associations. When match, remove job item index from data structure. If #items == #job_items, unregister the planned building. Before doing a filter match, peek the queue and discard any references to buildings that are no longer registered. Prune empty branches/mappings as we find matches so our top level map will be empty when we have no work left to do.

myk002 commented 3 years ago

ok, I think this series of PRs is ready for review. here's a blueprint that might be useful for verification: https://docs.google.com/spreadsheets/d/1hwr_CWd2OQLiAaR0sUn2vAmbeBQrm2UC5CkzKxDZ8OE/edit?usp=sharing

needs the following aliases in your aliases.txt file:

querybuildprefix: ^b querybuildsuffix: {Enter 5}^^q^q^q

trackN: CT{Enter} trackS: CT{+ 1}{Enter} trackE: CT{+ 2}{Enter} trackW: CT{+ 3}{Enter} trackNS: CT{+ 4}{Enter} trackNE: CT{+ 5}{Enter} trackNW: CT{+ 6}{Enter} trackSE: CT{+ 7}{Enter} trackSW: CT{+ 8}{Enter} trackEW: CT{+ 9}{Enter} trackNSE: CT{+ 10}{Enter} trackNSW: CT{+ 11}{Enter} trackNEW: CT{+ 12}{Enter} trackSEW: CT{+ 13}{Enter} trackNSEW: CT{+ 14}{Enter} trackrampN: CT{+ 15}{Enter} trackrampS: CT{+ 15}{+ 1}{Enter} trackrampE: CT{+ 15}{+ 2}{Enter} trackrampW: CT{+ 15}{+ 3}{Enter} trackrampNS: CT{+ 15}{+ 4}{Enter} trackrampNE: CT{+ 15}{+ 5}{Enter} trackrampNW: CT{+ 15}{+ 6}{Enter} trackrampSE: CT{+ 15}{+ 7}{Enter} trackrampSW: CT{+ 15}{+ 8}{Enter} trackrampEW: CT{+ 15}{+ 9}{Enter} trackrampNSE: CT{+ 15}{+ 10}{Enter} trackrampNSW: CT{+ 15}{+ 11}{Enter} trackrampNEW: CT{+ 15}{+ 12}{Enter} trackrampSEW: CT{+ 15}{+ 13}{Enter} trackrampNSEW: CT{+ 15}{+ 14}{Enter}

myk002 commented 3 years ago

Expected behavior: "Build from ui" blueprint gives us reference buildings that are created "manually" All 1, all 2, etc are build via the api. Apply once next to the ui-built buildings with buildingplan disabled (to get pure "constructWithFilters" behavior) and another time next to that with buildingplan enabled.

Use quickfort orders and/or createitem to get the building materials

myk002 commented 3 years ago

Btw I'm working towards infrastructure to run this kind of stuff as an automated integration test. Once buildingplan is settled, I'll open a bug where we can discuss how unit/integration testing might work and how it could possibly integrate with the buildmaster verification. In the meantime, I'll study the existing test code a bit more.

lethosor commented 3 years ago

Sounds good. I've been meaning to get through your PRs - I'll start with part 1 and see how it goes.

Tests would definitely be good. The test framework does support transitioning to an active fortress (or any other game mode), in theory, but that hasn't been implemented yet. One of the larger open questions there is when/how to "reset" a fortress to keep tests from breaking each other. Happy to discuss more once we get into that.