Skip to content

Commit f56e5e8

Browse files
authored
Instrument problems at wrong waypoints (#301)
* new logic to replace an incompatbile waypoint and instrument problem with a general problem instead * update tests
1 parent 91d0b38 commit f56e5e8

File tree

2 files changed

+69
-0
lines changed

2 files changed

+69
-0
lines changed

src/virtualship/make_realistic/problems/simulator.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,26 @@ def select_problems(
181181
else:
182182
if available_idxs:
183183
wp_select = random.choice(available_idxs)
184+
185+
# fmt: off
186+
# check waypoint actually deploys the instrument associated with the problem...if not, replace it with a general (non-instrument related) problem
187+
# rather than a different waypoint, because it's possible no applicable waypoint is still available
188+
wp_instruments = self.expedition.schedule.waypoints[wp_select].instrument
189+
if isinstance(problem, InstrumentProblem) and problem.instrument_type not in wp_instruments:
190+
available_general = [p for p in GENERAL_PROBLEMS if not p.pre_departure and p not in selected_problems]
191+
192+
if not available_general:
193+
unassigned_problems.append(problem)
194+
continue
195+
196+
replacement = random.choice(available_general)
197+
problem_idx = selected_problems.index(problem)
198+
selected_problems[problem_idx] = replacement
199+
# fmt: on
200+
184201
waypoint_idxs.append(wp_select)
185202
available_idxs.remove(wp_select) # each waypoint only used once
203+
186204
else:
187205
unassigned_problems.append(
188206
problem

tests/make_realistic/problems/test_simulator.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,54 @@ def test_post_expedition_report(tmp_path):
267267
assert problem.message in content, (
268268
"Problem messages in report should match those of selected problems."
269269
)
270+
271+
272+
def test_instrument_problems_only_selected_when_instruments_present(tmp_path):
273+
expedition = _make_simple_expedition(num_waypoints=3, no_instruments=True)
274+
instruments_in_expedition = expedition.get_instruments()
275+
assert len(instruments_in_expedition) == 0, "Expedition should have no instruments"
276+
277+
simulator = ProblemSimulator(expedition, str(tmp_path))
278+
problems = simulator.select_problems(
279+
instruments_in_expedition, difficulty_level="hard"
280+
)
281+
282+
has_instrument_problems = any(
283+
isinstance(cls, InstrumentProblem) for cls in problems["problem_class"]
284+
)
285+
assert not has_instrument_problems, (
286+
"Should not select instrument problems when no instruments are present"
287+
)
288+
289+
290+
def test_instrument_not_present_doesnt_select_instrument_problem(tmp_path):
291+
expedition = _make_simple_expedition(num_waypoints=3, no_instruments=True)
292+
293+
# prescribe instruments at waypoints, for this test case each should only be present at one waypoint
294+
expedition.schedule.waypoints[0].instrument = [InstrumentType.CTD]
295+
expedition.schedule.waypoints[1].instrument = [
296+
InstrumentType.ARGO_FLOAT,
297+
InstrumentType.DRIFTER,
298+
]
299+
300+
instruments_in_expedition = expedition.get_instruments()
301+
simulator = ProblemSimulator(expedition, str(tmp_path))
302+
303+
# run many iterations of randomly selecting problems and check that if an instrument problem is selected, the associated instrument is actually present at the selected waypoint
304+
for _ in range(int(1e4)):
305+
problems = simulator.select_problems(
306+
instruments_in_expedition, difficulty_level="hard"
307+
)
308+
309+
for problem, wp_i in zip(
310+
problems["problem_class"], problems["waypoint_i"], strict=False
311+
):
312+
if isinstance(problem, InstrumentProblem):
313+
wp_instruments = expedition.schedule.waypoints[wp_i].instrument
314+
assert problem.instrument_type in wp_instruments, (
315+
"Instrument problem should only be selected if the instrument is present at the selected waypoint"
316+
)
317+
318+
# any incompatible waypoint x instrument problem combinations should have been replaced by a general problem
319+
else:
320+
assert isinstance(problem, GeneralProblem)

0 commit comments

Comments
 (0)