Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/virtualship/make_realistic/problems/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,26 @@ def select_problems(
else:
if available_idxs:
wp_select = random.choice(available_idxs)

# fmt: off
# check waypoint actually deploys the instrument associated with the problem...if not, replace it with a general (non-instrument related) problem
# rather than a different waypoint, because it's possible no applicable waypoint is still available
wp_instruments = self.expedition.schedule.waypoints[wp_select].instrument
if isinstance(problem, InstrumentProblem) and problem.instrument_type not in wp_instruments:
available_general = [p for p in GENERAL_PROBLEMS if not p.pre_departure and p not in selected_problems]

if not available_general:
unassigned_problems.append(problem)
continue

replacement = random.choice(available_general)
problem_idx = selected_problems.index(problem)
selected_problems[problem_idx] = replacement
# fmt: on

waypoint_idxs.append(wp_select)
available_idxs.remove(wp_select) # each waypoint only used once

else:
unassigned_problems.append(
problem
Expand Down
51 changes: 51 additions & 0 deletions tests/make_realistic/problems/test_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,54 @@ def test_post_expedition_report(tmp_path):
assert problem.message in content, (
"Problem messages in report should match those of selected problems."
)


def test_instrument_problems_only_selected_when_instruments_present(tmp_path):
expedition = _make_simple_expedition(num_waypoints=3, no_instruments=True)
instruments_in_expedition = expedition.get_instruments()
assert len(instruments_in_expedition) == 0, "Expedition should have no instruments"

simulator = ProblemSimulator(expedition, str(tmp_path))
problems = simulator.select_problems(
instruments_in_expedition, difficulty_level="hard"
)

has_instrument_problems = any(
isinstance(cls, InstrumentProblem) for cls in problems["problem_class"]
)
assert not has_instrument_problems, (
"Should not select instrument problems when no instruments are present"
)


def test_instrument_not_present_doesnt_select_instrument_problem(tmp_path):
expedition = _make_simple_expedition(num_waypoints=3, no_instruments=True)

# prescribe instruments at waypoints, for this test case each should only be present at one waypoint
expedition.schedule.waypoints[0].instrument = [InstrumentType.CTD]
expedition.schedule.waypoints[1].instrument = [
InstrumentType.ARGO_FLOAT,
InstrumentType.DRIFTER,
]

instruments_in_expedition = expedition.get_instruments()
simulator = ProblemSimulator(expedition, str(tmp_path))

# 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
for _ in range(int(1e4)):
problems = simulator.select_problems(
instruments_in_expedition, difficulty_level="hard"
)

for problem, wp_i in zip(
problems["problem_class"], problems["waypoint_i"], strict=False
):
if isinstance(problem, InstrumentProblem):
wp_instruments = expedition.schedule.waypoints[wp_i].instrument
assert problem.instrument_type in wp_instruments, (
"Instrument problem should only be selected if the instrument is present at the selected waypoint"
)

# any incompatible waypoint x instrument problem combinations should have been replaced by a general problem
else:
assert isinstance(problem, GeneralProblem)