Balance crop section maturity and distance
This commit is contained in:
+41
-13
@@ -18,6 +18,7 @@ from mosfet.info import mobs
|
||||
|
||||
class GatherCropStates:
|
||||
CROPS_PER_BATCH = 16
|
||||
SECTION_MATURITY_WEIGHT = 2.0
|
||||
|
||||
def idle(self):
|
||||
return None
|
||||
@@ -57,28 +58,50 @@ class GatherCropStates:
|
||||
if position not in self.bad_crops and
|
||||
position in component_for]
|
||||
if not eligible:
|
||||
self.active_section_maturity = (0, 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_candidates = [position for position in eligible
|
||||
if component_for[position] == active_component]
|
||||
active_candidates = candidates_by_component.get(active_component, [])
|
||||
if active_candidates:
|
||||
section_candidates = active_candidates
|
||||
selected_component = active_component
|
||||
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
|
||||
]
|
||||
# Favor a nearby section if maturity is similar, but allow a
|
||||
# farther, substantially more-mature section to win. Once chosen,
|
||||
# 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))
|
||||
distance = utils.phyp(center, nearest_crop)
|
||||
maturity = (mature_by_component[component] /
|
||||
component_sizes[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: (
|
||||
utils.phyp(center, position), position))
|
||||
section_size = component_sizes[selected_component]
|
||||
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):
|
||||
print('Finding new crop...')
|
||||
@@ -111,8 +134,12 @@ class GatherCropStates:
|
||||
self.state = self.cleanup
|
||||
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,
|
||||
'(same-species section size={})'.format(section_size))
|
||||
'(same-species section: {}/{} mature, {:.0f}%)'.format(
|
||||
mature_count, total_count, maturity_percent))
|
||||
self.crop = crop
|
||||
self.type_id = w.block_at(*crop)
|
||||
self.state = self.nav_to_crop
|
||||
@@ -248,6 +275,7 @@ class GatherCropStates:
|
||||
self.seed_deadline = None
|
||||
self.seed_wait_logged = False
|
||||
self.active_section_anchor = None
|
||||
self.active_section_maturity = (0, 0)
|
||||
self.bad_crops = []
|
||||
self.completed_crops = 0
|
||||
|
||||
|
||||
@@ -43,6 +43,26 @@ class TestCropSectionPriority(unittest.TestCase):
|
||||
self.assertEqual(next_patch, carrot_a)
|
||||
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):
|
||||
mature = (0, 64, 0)
|
||||
replanted = (1, 64, 0)
|
||||
|
||||
Reference in New Issue
Block a user