Balance crop section maturity and distance

This commit is contained in:
Tanner
2026-11-09 01:21:44 +00:00
parent b642204ced
commit 7e43001d9d
2 changed files with 61 additions and 13 deletions
+40 -12
View File
@@ -18,6 +18,7 @@ from mosfet.info import mobs
class GatherCropStates: class GatherCropStates:
CROPS_PER_BATCH = 16 CROPS_PER_BATCH = 16
SECTION_MATURITY_WEIGHT = 2.0
def idle(self): def idle(self):
return None return None
@@ -57,28 +58,50 @@ class GatherCropStates:
if position not in self.bad_crops and if position not in self.bad_crops and
position in component_for] position in component_for]
if not eligible: if not eligible:
self.active_section_maturity = (0, 0)
return None, 0 return None, 0
mature_by_component = {}
candidates_by_component = {}
for position in eligible:
component = component_for[position]
mature_by_component[component] = (
mature_by_component.get(component, 0) + 1)
candidates_by_component.setdefault(component, []).append(position)
active_component = component_for.get(self.active_section_anchor) active_component = component_for.get(self.active_section_anchor)
active_candidates = [position for position in eligible active_candidates = candidates_by_component.get(active_component, [])
if component_for[position] == active_component]
if active_candidates: if active_candidates:
section_candidates = active_candidates section_candidates = active_candidates
selected_component = active_component
else: else:
# Begin with the nearest available patch. Once selected, keep its # Favor a nearby section if maturity is similar, but allow a
# connected component active until no mature crops remain there. # farther, substantially more-mature section to win. Once chosen,
first_crop = min(eligible, key=lambda position: ( # keep working that connected component until it has no mature
# crops left, matching the existing patch-stability behavior.
ranked_sections = []
for component, positions in candidates_by_component.items():
nearest_crop = min(positions, key=lambda position: (
utils.phyp(center, position), position)) utils.phyp(center, position), position))
selected_component = component_for[first_crop] distance = utils.phyp(center, nearest_crop)
section_candidates = [ maturity = (mature_by_component[component] /
position for position in eligible component_sizes[component])
if component_for[position] == selected_component score = distance / (
] 1.0 + self.SECTION_MATURITY_WEIGHT * maturity)
ranked_sections.append((
score, distance, -mature_by_component[component],
nearest_crop, component,
))
selected_component = min(ranked_sections)[-1]
section_candidates = candidates_by_component[selected_component]
crop = min(section_candidates, key=lambda position: ( crop = min(section_candidates, key=lambda position: (
utils.phyp(center, position), position)) utils.phyp(center, position), position))
section_size = component_sizes[selected_component]
self.active_section_anchor = crop self.active_section_anchor = crop
return crop, component_sizes[component_for[crop]] self.active_section_maturity = (
mature_by_component[selected_component], section_size)
return crop, section_size
def find_new_crop(self): def find_new_crop(self):
print('Finding new crop...') print('Finding new crop...')
@@ -111,8 +134,12 @@ class GatherCropStates:
self.state = self.cleanup self.state = self.cleanup
return return
mature_count, total_count = self.active_section_maturity
maturity_percent = (100 * mature_count / total_count
if total_count else 0)
print('Found crop:', crop, print('Found crop:', crop,
'(same-species section size={})'.format(section_size)) '(same-species section: {}/{} mature, {:.0f}%)'.format(
mature_count, total_count, maturity_percent))
self.crop = crop self.crop = crop
self.type_id = w.block_at(*crop) self.type_id = w.block_at(*crop)
self.state = self.nav_to_crop self.state = self.nav_to_crop
@@ -248,6 +275,7 @@ class GatherCropStates:
self.seed_deadline = None self.seed_deadline = None
self.seed_wait_logged = False self.seed_wait_logged = False
self.active_section_anchor = None self.active_section_anchor = None
self.active_section_maturity = (0, 0)
self.bad_crops = [] self.bad_crops = []
self.completed_crops = 0 self.completed_crops = 0
+20
View File
@@ -43,6 +43,26 @@ class TestCropSectionPriority(unittest.TestCase):
self.assertEqual(next_patch, carrot_a) self.assertEqual(next_patch, carrot_a)
self.assertEqual(next_patch_size, 4) self.assertEqual(next_patch_size, 4)
def test_farther_section_wins_when_much_more_mature(self):
near_patch = {
(3, 64, 0), (2, 64, 0), (1, 64, 0), (0, 64, 0),
(3, 64, 1), (2, 64, 1), (1, 64, 1), (0, 64, 1),
(3, 64, 2), (2, 64, 2),
}
near_mature = (3, 64, 0)
far_patch = {
(4, 64, 0), (5, 64, 0), (4, 64, 1), (5, 64, 1),
}
crop_types = {position: 'wheat' for position in near_patch}
crop_types.update({position: 'carrots' for position in far_patch})
crop, size = self.job._select_crop(
[near_mature, *far_patch], crop_types, (0, 64, 0))
self.assertEqual(crop, (4, 64, 0))
self.assertEqual(size, len(far_patch))
self.assertEqual(self.job.active_section_maturity, (4, 4))
def test_unripe_same_species_blocks_keep_patch_connected(self): def test_unripe_same_species_blocks_keep_patch_connected(self):
mature = (0, 64, 0) mature = (0, 64, 0)
replanted = (1, 64, 0) replanted = (1, 64, 0)