Select adjacent crops first

This commit is contained in:
Tanner
2026-11-04 15:21:59 +00:00
parent f45a1b6b82
commit 563aa53eec
3 changed files with 133 additions and 5 deletions
+1
View File
@@ -280,4 +280,5 @@ class JobStates(LegacyJobStates):
self.g.path = []
self.g.look_at = None
self.gather_crop_states.state = self.gather_crop_states.idle
self.gather_crop_states.active_section_anchor = None
self.state = self.idle
+67 -5
View File
@@ -23,6 +23,61 @@ class GatherCropStates:
def init(self):
self.state = self.find_new_crop
def _select_crop(self, candidates, crop_types, center):
"""Stay with a connected same-species patch, nearest patch first."""
adjacent_offsets = ((1, 0, 0), (-1, 0, 0),
(0, 0, 1), (0, 0, -1))
remaining = set(crop_types)
component_for = {}
component_sizes = {}
component_id = 0
while remaining:
seed = remaining.pop()
species = crop_types[seed]
stack = [seed]
component = [seed]
component_for[seed] = component_id
while stack:
x, y, z = stack.pop()
for dx, dy, dz in adjacent_offsets:
adjacent = (x + dx, y + dy, z + dz)
if (adjacent in remaining and
crop_types[adjacent] == species):
remaining.remove(adjacent)
component_for[adjacent] = component_id
component.append(adjacent)
stack.append(adjacent)
component_sizes[component_id] = len(component)
component_id += 1
eligible = [position for position in candidates
if position not in self.bad_crops and
position in component_for]
if not eligible:
return None, 0
active_component = component_for.get(self.active_section_anchor)
active_candidates = [position for position in eligible
if component_for[position] == active_component]
if active_candidates:
section_candidates = active_candidates
else:
# Begin with the nearest available patch. Once selected, keep its
# connected component active until no mature crops remain there.
first_crop = min(eligible, key=lambda position: (
utils.phyp(center, position), position))
selected_component = component_for[first_crop]
section_candidates = [
position for position in eligible
if component_for[position] == selected_component
]
crop = min(section_candidates, key=lambda position: (
utils.phyp(center, position), position))
self.active_section_anchor = crop
return crop, component_sizes[component_for[crop]]
def find_new_crop(self):
print('Finding new crop...')
w = self.g.world
@@ -36,17 +91,23 @@ class GatherCropStates:
]
candidates = w.find_blocks_indexed(p, mature_crops, 50)
crop_ids = (blocks.WHEAT_IDS | blocks.POTATO_IDS |
blocks.CARROT_IDS | blocks.BEETROOT_IDS)
crop_positions = w.find_blocks_indexed(p, crop_ids, 50)
crop_types = {
position: blocks.BLOCKS.get(w.block_at(*position))
for position in crop_positions
}
crop, section_size = self._select_crop(candidates, crop_types, p)
for crop in candidates:
if crop not in self.bad_crops:
break
else: # for
if crop is None:
print('No good crops left; retrying after a short wait.')
self.retry_after = time.monotonic() + 15.0
self.state = self.wait_for_crops
return
print('Found crop:', crop)
print('Found crop:', crop,
'(same-species section size={})'.format(section_size))
self.crop = crop
self.type_id = w.block_at(*crop)
self.state = self.nav_to_crop
@@ -178,6 +239,7 @@ class GatherCropStates:
self.target_seed = None
self.seed_deadline = None
self.seed_wait_logged = False
self.active_section_anchor = None
self.retry_after = 0.0
self.bad_crops = []