19declare(strict_types=1);
87 public function preload(array $items): void
90 foreach ($items as $item) {
91 $ref_ids[] = $item->getRefId();
93 $ref_ids = array_values(array_unique($ref_ids));
94 $missing_ref_ids = array_values(array_filter(
95 array_unique($ref_ids),
96 fn(
int $ref_id):
bool => !isset($this->conditions_cache[$ref_id])
99 $this->edge_targets_cache = [];
100 $this->dependency_targets_cache = [];
101 $this->points_structural_successors_cache = [];
102 $this->points_structural_predecessors_cache = [];
103 $this->can_leave_cache = [];
104 $this->can_enter_ignoring_edges_cache = [];
106 if ($ref_ids === []) {
110 if ($missing_ref_ids !== []) {
111 $ids_per_item = $this->condition_factory->preloadConditionIdsForItems($missing_ref_ids);
113 foreach ($ids_per_item as $ref_id => $ids) {
115 foreach ($ids as
$id) {
117 $conditions[] = $this->condition_factory->getConditionInstanceById(
$id);
118 }
catch (\Throwable $t) {
122 $this->conditions_cache[$ref_id] = $conditions;
139 foreach ($items as $item) {
140 if ($item->getRefId() === $current->
getRefId()) {
145 || in_array($current->
getRefId(), $this->getDependencyTargetsFor($item->getRefId()),
true)
148 $successors[$item->getRefId()] = $item;
154 $successors[$item->getRefId()] = $item;
157 return array_values($successors);
169 foreach ($items as $item) {
170 if ($item->getRefId() === $current->
getRefId()) {
175 || in_array($current->
getRefId(), $this->getDependencyTargetsFor($item->getRefId()),
true)
176 || in_array($item->getRefId(), $this->getPointsStructuralSuccessorsFor($current->
getRefId()),
true)
178 $successors[] = $item;
192 $predecessor_ref_ids = array_values(array_unique(array_merge(
194 $this->getDependencyTargetsFor($current->
getRefId()),
195 $this->getPointsStructuralPredecessorsFor($current->
getRefId())
198 foreach ($items as $item) {
199 if (in_array($item->getRefId(), $predecessor_ref_ids,
true)) {
200 $predecessors[] = $item;
203 return $predecessors;
212 if (isset($this->can_leave_cache[$ref_id])) {
213 return $this->can_leave_cache[$ref_id];
224 $this->can_leave_cache[$ref_id] = $can_leave;
247 if (isset($this->can_enter_ignoring_edges_cache[$ref_id])) {
248 return $this->can_enter_ignoring_edges_cache[$ref_id];
262 $this->can_enter_ignoring_edges_cache[$ref_id] = $can_enter;
272 $has_alternative_edge_condition = array_any(
276 $matches_alternative_edge_condition =
false;
277 $has_dependency_condition =
false;
278 $matches_dependency_condition =
false;
280 foreach ($conditions as $condition) {
289 $matches_alternative_edge_condition =
true;
297 $has_dependency_condition =
true;
299 $matches_dependency_condition =
true;
308 if ($has_alternative_edge_condition) {
309 return $matches_alternative_edge_condition;
312 if ($has_dependency_condition && !$matches_dependency_condition) {
329 $id = $condition->getConditionId();
348 $id = $condition->getConditionId();
362 $has_output_condition =
false;
367 $has_output_condition =
true;
372 return !$has_output_condition;
384 foreach ($items as $item) {
390 $ref_ids[] = $item->getRefId();
396 return array_values(array_unique($ref_ids));
404 return array_values(array_filter(
416 return $condition->
check();
417 }
catch (\Throwable $t) {
425 protected function isEdge(
int $from_ref_id,
int $to_ref_id): bool
437 if (isset($this->edge_targets_cache[$item_ref_id])) {
438 return $this->edge_targets_cache[$item_ref_id];
441 return $this->edge_targets_cache[$item_ref_id] = [];
449 return $this->dependency_targets_cache[$item_ref_id] ?? [];
457 $this->edge_targets_cache = [];
458 $this->dependency_targets_cache = [];
460 foreach ($item_ref_ids as $item_ref_id) {
462 $dependency_targets = [];
469 $source_ref_ids = array_values(array_unique(array_map(
'intval', $condition->getNavigationSourceRefIds())));
470 if ($source_ref_ids === []) {
475 $edge_targets = array_values(array_unique(array_merge($edge_targets, $source_ref_ids)));
480 $dependency_targets = array_values(array_unique(array_merge($dependency_targets, $source_ref_ids)));
484 $this->edge_targets_cache[$item_ref_id] = $edge_targets;
485 $this->dependency_targets_cache[$item_ref_id] = $dependency_targets;
496 $this->points_structural_successors_cache = [];
497 $this->points_structural_predecessors_cache = [];
498 $this->max_points_budget = 0;
500 foreach ($items as $item) {
501 $ref_id = $item->getRefId();
502 $this->points_structural_successors_cache[$ref_id] = [];
503 $this->points_structural_predecessors_cache[$ref_id] = [];
513 foreach ($items as $item) {
514 $ref_id = $item->getRefId();
523 $seen_states[$ref_id][$points_after] =
true;
524 $queue[] = [$ref_id, $points_after];
527 while ($queue !== []) {
528 [$current_ref_id, $current_points_after] = array_shift($queue);
532 $target_ref_id =
$target->getRefId();
533 if ($target_ref_id === $current_ref_id) {
537 if ($this->
isEdge($current_ref_id, $target_ref_id)) {
539 $candidate_points = min(
540 $this->max_points_budget,
543 if (!isset($seen_states[$target_ref_id][$candidate_points])) {
544 $seen_states[$target_ref_id][$candidate_points] =
true;
545 $queue[] = [$target_ref_id, $candidate_points];
552 if ($required_points ===
null || $required_points === 0) {
555 if ($current_points_before >= $required_points || $current_points_after < $required_points) {
561 $candidate_points = min(
562 $this->max_points_budget,
565 if (!isset($seen_states[$target_ref_id][$candidate_points])) {
566 $seen_states[$target_ref_id][$candidate_points] =
true;
567 $queue[] = [$target_ref_id, $candidate_points];
575 if (!in_array($to_ref_id, $this->points_structural_successors_cache[$from_ref_id] ?? [],
true)) {
576 $this->points_structural_successors_cache[$from_ref_id][] = $to_ref_id;
578 if (!in_array($from_ref_id, $this->points_structural_predecessors_cache[$to_ref_id] ?? [],
true)) {
579 $this->points_structural_predecessors_cache[$to_ref_id][] = $from_ref_id;
606 if ($required_points ===
null) {
610 return $available_points >= $required_points;
618 $required_points = 0;
630 $required_points = max($required_points, $condition->getRequiredAccumulatedValue());
638 return $required_points;
650 $points += $condition->getAccumulatedValue();
651 }
catch (\Throwable $t) {
665 return $this->points_structural_successors_cache[$item_ref_id] ?? [];
673 return $this->points_structural_predecessors_cache[$item_ref_id] ?? [];
691 && in_array($current_ref_id, $condition->getNavigationSourceRefIds(),
true);
706 if (isset($this->conditions_cache[$item_ref_id])) {
707 return $this->conditions_cache[$item_ref_id];
711 foreach ($this->condition_factory->getConditionIdsForItem($item_ref_id) as $condition_id) {
713 $conditions[] = $this->condition_factory->getConditionInstanceById($condition_id);
714 }
catch (\Throwable $t) {
719 $this->conditions_cache[$item_ref_id] = $conditions;
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
check()
Checks if the condition is fulfilled.
Class AlwaysOutputCondition.
Provides condition-based navigation through an adaptive learning sequence.
getInputConditions(\LSItem $item)
isEdge(int $from_ref_id, int $to_ref_id)
Determines whether an edge connects two items.
buildPointsNavigationCaches(array $items)
Precomputes synthetic points-based unlock edges for map rendering.
canLeave(\LSLearnerItem $current)
Determines whether all output conditions of the current item are met.
canEnterFrom(\LSLearnerItem $current, \LSLearnerItem $target)
Determines whether the target is enterable from the current item.
array $points_structural_successors_cache
preload(array $items)
Preloads condition instances for the given items.
getPointsStructuralPredecessorsFor(int $item_ref_id)
getOutputConditionIds(\LSLearnerItem $item)
Returns the identifiers of output conditions assigned to an item.
getInputConditionIds(\LSLearnerItem $item)
Returns the identifiers of input conditions assigned to an item.
isAlternativeEdgeCondition(AbstractCondition $condition)
array $can_enter_ignoring_edges_cache
canEnter(\LSLearnerItem $target)
Determines whether all input conditions of the target item are met.
array $points_structural_predecessors_cache
checkCondition(AbstractCondition $condition)
Evaluates a condition and treats evaluation failures as unmet conditions.
getConfiguredPointsOutput(int $item_ref_id)
requiresVisitForCompletion(\LSLearnerItem $item)
Determines whether completion is only proven by visiting the item.
getPointsStructuralSuccessorsFor(int $item_ref_id)
isDependencyCondition(AbstractCondition $condition)
array $dependency_targets_cache
buildNavigationSourceCaches(array $item_ref_ids)
isGlobalSuccessorTarget(int $target_ref_id)
Returns whether the target behaves like a global points gate in the player.
getPointsOutputRefIds(array $items)
Returns the reference identifiers of items with a configured points output condition.
getPredecessors(array $items, \LSLearnerItem $current)
Returns the items that are connected to the current item by an incoming edge.
getEdgeTargetsFor(int $item_ref_id)
Returns the source reference identifiers of edges leading to an item.
getConditionsFor(int $item_ref_id)
Returns the condition instances assigned to an item.
getSuccessors(array $items, \LSLearnerItem $current)
Returns the items connected to and enterable from the current item.
conditionReferencesCurrent(AbstractCondition $condition, int $current_ref_id)
__construct(ConditionFactory $condition_factory)
addPointsStructuralEdge(int $from_ref_id, int $to_ref_id)
getStructuralSuccessors(array $items, \LSItem $current)
Returns the items structurally connected to the current item.
canStructurallyEnterWithAvailablePoints(int $target_ref_id, int $available_points)
Returns whether the item can be entered using points-only structural data.
canEnterIgnoringEdges(\LSLearnerItem $target)
Determines whether the target is enterable without learning-progress edge conditions.
getPureAccumulatedRequirement(int $target_ref_id, string $accumulation_identifier)
Returns the required points if the item's non-edge inputs are points-only.
hasSourceScopedNavigation(int $target_ref_id)
array $edge_targets_cache
getDependencyTargetsFor(int $item_ref_id)
ConditionFactory $condition_factory
Creates condition instances from their identifiers.
Data holding class LSItem .
Add learning progress and availability information to the LSItem.
getAccumulationIdentifier()
Marker interface for input conditions in the Learning Sequence condition system.
const string NAVIGATION_MODE_GLOBAL
const string NAVIGATION_MODE_DEPENDENCY
const string NAVIGATION_MODE_EDGE
getAccumulationIdentifier()
Marker interface for output conditions in the Learning Sequence condition system.
Abstraction of the navigation between the objects of a learning sequence.