Repository: cs-24-sw-9-xx/MAEPS
Pull request #263: Make PatrollingAlgorithm Component based
Author: login: mads256h
Created: 2025-03-05T14:18:14Z | Merged: 2025-03-06T10:49:28Z

Components have two methods `PreUpdate` and `PostUpdate`.

`PreUpdate` is called before the algorithm's `Update` method is called.
Here the component do stuff such as move the robot about.
It can also wait for certain conditions such as a robot status or waiting for x ticks.
Likewise `PostUpdate` is run after the algorithm's `Update` method is called.

`PreUpdate` and `PostUpdate` are enumerables so that we can implement statemachines without having to keep track of the machine's state manually.

They return a wrapper around `WaitForCondition` with an additional bool `ShouldContinue`.
If `ShouldContinue` is true the next component in the chain is run, and eventually `Update` is run.

If `ShouldContinue` is false the chain stops executing there and next tick `PreUpdate` is called again.
Here `Update` and `PostUpdate` is not called.
This allows the components to do exceptional things such as collision avoidance, while not being trampled over by the next components.

Our tests seem to be shit as we have no collision avoidance right now.
Are there really no problems having no collision avoidance? I really doubt it!

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Introduced a modular navigation component for patrolling operations to streamline pathfinding.

- **Refactor**
  - Enhanced several patrol algorithms for better modularity, encapsulation, and overall stability.
  - Updated access levels and internal logic to ensure more consistent behavior.
  - Adjusted the visualization mode by temporarily disabling target point display to align with the updated algorithm design.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/Assets/Scripts/Algorithms/IPatrollingAlgorithm.cs b/Assets/Scripts/Algorithms/IPatrollingAlgorithm.cs
index 25c38ca48..fbba33ec2 100644
--- a/Assets/Scripts/Algorithms/IPatrollingAlgorithm.cs
+++ b/Assets/Scripts/Algorithms/IPatrollingAlgorithm.cs
@@ -8,6 +8,9 @@ public interface IPatrollingAlgorithm : IAlgorithm
     {
         string AlgorithmName { get; }
 
+        /// <summary>
+        /// Only to be used for visualization.
+        /// </summary>
         Vertex TargetVertex { get; }
 
         void SetPatrollingMap(PatrollingMap map);
diff --git a/Assets/Scripts/Algorithms/Patrolling/CognitiveCoordinated.cs b/Assets/Scripts/Algorithms/Patrolling/CognitiveCoordinated.cs
index 947a3ef61..77883bba3 100644
--- a/Assets/Scripts/Algorithms/Patrolling/CognitiveCoordinated.cs
+++ b/Assets/Scripts/Algorithms/Patrolling/CognitiveCoordinated.cs
@@ -3,13 +3,15 @@
 using System.Linq;
 using System.Text;
 
+using Maes.Algorithms.Patrolling.Components;
 using Maes.Map;
+using Maes.Robot;
 
 using UnityEngine;
 
 namespace Maes.Algorithms.Patrolling
 {
-    public class CognitiveCoordinated : PatrollingAlgorithm
+    public sealed class CognitiveCoordinated : PatrollingAlgorithm
     {
         public override string AlgorithmName => "Cognitive Coordinated Algorithm";
         private List<Vertex> _currentPath = new();
@@ -26,6 +28,16 @@ protected override void GetDebugInfo(StringBuilder stringBuilder)
                 .AppendFormat("Last Vertex: {0}\n", _currentPath.Count > 0 ? _currentPath[^1].ToString() : "None");
         }
 
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
+        protected override IComponent[] CreateComponents(Robot2DController controller, PatrollingMap patrollingMap)
+        {
+            _goToNextVertexComponent = new GoToNextVertexComponent(NextVertex, this, controller, patrollingMap);
+
+            return new IComponent[] { _goToNextVertexComponent };
+        }
+
         public override void SetGlobalPatrollingMap(PatrollingMap globalMap)
         {
             base.SetGlobalPatrollingMap(globalMap);
@@ -39,7 +51,7 @@ public override void SetGlobalPatrollingMap(PatrollingMap globalMap)
             Coordinator.ClearOccupiedVertices();
         }
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        private Vertex NextVertex(Vertex currentVertex)
         {
             // We have reached our target. Create a new path.
             if (_pathStep == _currentPath.Count)
diff --git a/Assets/Scripts/Algorithms/Patrolling/Components.meta b/Assets/Scripts/Algorithms/Patrolling/Components.meta
new file mode 100644
index 000000000..86721c6ef
--- /dev/null
+++ b/Assets/Scripts/Algorithms/Patrolling/Components.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d5e0bcfb91b8844ea9857ada0c13c848
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs b/Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs
new file mode 100644
index 000000000..d4c4515cd
--- /dev/null
+++ b/Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs
@@ -0,0 +1,188 @@
+// Copyright 2025 MAEPS
+// 
+// This file is part of MAEPS
+// 
+// MAEPS is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the
+// Free Software Foundation, either version 3 of the License, or (at your option)
+// any later version.
+// 
+// MAEPS is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+// Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License along
+// with MAEPS. If not, see http://www.gnu.org/licenses/.
+// 
+// Contributors: Mads Beyer Mogensen
+
+using System;
+using System.Collections.Generic;
+
+using Maes.Map;
+using Maes.Map.PathFinding;
+using Maes.Robot;
+using Maes.Robot.Task;
+
+using UnityEngine;
+
+namespace Maes.Algorithms.Patrolling.Components
+{
+    /// <summary>
+    /// This component moves the robot to the vertex specified by the algorithm.
+    /// When the vertex has been reached it gets the next vertex from the algorithm.
+    /// </summary>
+    public sealed class GoToNextVertexComponent : IComponent
+    {
+        // How close the robot has to get to a point before it has arrived.
+        private const float MinDistance = 0.25f;
+
+        // How small the angle to the target has to be before it is pointing in the direction.
+        private const float MinAngle = 1.5f;
+
+        private readonly NextVertexDelegate _nextVertexDelegate;
+        private readonly PatrollingAlgorithm _patrollingAlgorithm;
+        private readonly Robot2DController _controller;
+        private readonly PatrollingMap _patrollingMap;
+
+        /// <summary>
+        /// Delegate that specifies the next vertex to travel to.
+        /// </summary>
+        public delegate Vertex NextVertexDelegate(Vertex currentVertex);
+
+        /// <inheritdoc />
+        public int PreUpdateOrder { get; } = 0;
+
+        /// <inheritdoc />
+        public int PostUpdateOrder { get; } = 0;
+
+        /// <summary>
+        /// Creates a new instance of <see cref="GoToNextVertexComponent"/>.
+        /// </summary>
+        /// <param name="nextVertexDelegate">The delegate that gets called to get the next vertex.</param>
+        /// <param name="patrollingAlgorithm">The patrolling algorithm.</param>
+        /// <param name="controller">The robot controller.</param>
+        /// <param name="patrollingMap">The patrolling map.</param>
+        public GoToNextVertexComponent(NextVertexDelegate nextVertexDelegate, PatrollingAlgorithm patrollingAlgorithm, Robot2DController controller, PatrollingMap patrollingMap)
+        {
+            _nextVertexDelegate = nextVertexDelegate;
+            _patrollingAlgorithm = patrollingAlgorithm;
+            _controller = controller;
+            _patrollingMap = patrollingMap;
+        }
+
+        /// <inheritdoc />
+        public IEnumerable<ComponentWaitForCondition> PreUpdateLogic()
+        {
+            // Go to the initial vertex.
+            var vertex = GetClosestVertex();
+            while (GetRelativePositionTo(vertex.Position).Distance > MinDistance)
+            {
+                _controller.PathAndMoveTo(vertex.Position, dependOnBrokenBehaviour: false);
+                yield return ComponentWaitForCondition.WaitForLogicTicks(1, shouldContinue: false);
+            }
+
+            while (true)
+            {
+
+                // Follow the path.
+                // Go to the next vertex
+                var targetVertex = _nextVertexDelegate(vertex);
+
+                // Tell PatrollingAlgorithm that we reached the vertex
+                _patrollingAlgorithm.OnReachTargetVertex(vertex, targetVertex);
+
+                var path = GetPathStepsToVertex(vertex, targetVertex);
+                vertex = targetVertex;
+
+
+                // Move to the start of the path
+                foreach (var condition in MoveToPosition(path.Peek().Start))
+                {
+                    yield return condition;
+                }
+
+                // Move to the end of each path
+                while (path.TryDequeue(out var pathStep))
+                {
+                    foreach (var condition in MoveToPosition(pathStep.End))
+                    {
+                        yield return condition;
+                    }
+                }
+            }
+        }
+
+        /// <inheritdoc />
+        public IEnumerable<ComponentWaitForCondition> PostUpdateLogic()
+        {
+            while (true)
+            {
+                yield return ComponentWaitForCondition.WaitForLogicTicks(1, shouldContinue: true);
+            }
+        }
+
+        /// <summary>
+        /// Gets the vertex closest to the robot.
+        /// </summary>
+        /// <remarks>
+        /// TODO: This does not take walls into account so it might not pick the best waypoint.
+        /// </remarks>
+        /// <returns>The closest vertex.</returns>
+        private Vertex GetClosestVertex()
+        {
+            var vertices = _patrollingMap.Vertices;
+
+            var position = _controller.GetSlamMap().GetCoarseMap().GetCurrentPosition(dependOnBrokenBehavior: false);
+            var closestVertex = vertices[0];
+            var closestDistance = Vector2Int.Distance(position, closestVertex.Position);
+            foreach (var vertex in vertices.AsSpan(1))
+            {
+                var distance = Vector2Int.Distance(position, vertex.Position);
+                if (distance < closestDistance)
+                {
+                    closestDistance = distance;
+                    closestVertex = vertex;
+                }
+            }
+
+            return closestVertex;
+        }
+
+
+        private IEnumerable<ComponentWaitForCondition> MoveToPosition(Vector2Int target)
+        {
+            while (true)
+            {
+                yield return ComponentWaitForCondition.WaitForRobotStatus(RobotStatus.Idle, shouldContinue: false);
+
+                var relativePosition = GetRelativePositionTo(target);
+                if (relativePosition.Distance <= MinDistance)
+                {
+                    yield break;
+                }
+
+
+                if (Math.Abs(relativePosition.RelativeAngle) > MinAngle)
+                {
+                    _controller.Rotate(relativePosition.RelativeAngle);
+                }
+                else
+                {
+                    _controller.Move(relativePosition.Distance);
+                }
+            }
+        }
+
+        private RelativePosition GetRelativePositionTo(Vector2Int position)
+        {
+            return _controller.SlamMap.CoarseMap.GetTileCenterRelativePosition(position, dependOnBrokenBehaviour: false);
+        }
+
+        private Queue<PathStep> GetPathStepsToVertex(Vertex currentVertex, Vertex targetVertex)
+        {
+            return new Queue<PathStep>(_patrollingMap.Paths[(currentVertex.Id, targetVertex.Id)]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs.meta b/Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs.meta
new file mode 100644
index 000000000..8d1ac5f18
--- /dev/null
+++ b/Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 3f1a329c4d851a62bb38870880d49dfc
\ No newline at end of file
diff --git a/Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs b/Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs
new file mode 100644
index 000000000..10beeabed
--- /dev/null
+++ b/Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs
@@ -0,0 +1,69 @@
+// Copyright 2025 MAEPS
+// 
+// This file is part of MAEPS
+// 
+// MAEPS is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the
+// Free Software Foundation, either version 3 of the License, or (at your option)
+// any later version.
+// 
+// MAEPS is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+// Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License along
+// with MAEPS. If not, see http://www.gnu.org/licenses/.
+// 
+// Contributors: Mads Beyer Mogensen
+
+using System.Collections.Generic;
+
+using Maes.Robot.Task;
+
+namespace Maes.Algorithms.Patrolling.Components
+{
+    /// <summary>
+    /// A component that can be used in <see cref="PatrollingAlgorithm"/> to add functionality.
+    /// </summary>
+    public interface IComponent
+    {
+        IEnumerable<ComponentWaitForCondition> PreUpdateLogic();
+
+        IEnumerable<ComponentWaitForCondition> PostUpdateLogic();
+
+        /// <summary>
+        /// The order the component's <see cref="PreUpdateLogic"/> method is executed in comparison to the other components.
+        /// </summary>
+        int PreUpdateOrder { get; }
+
+        /// <summary>
+        /// The order the component's <see cref="PostUpdateLogic"/> method is executed in comparison to the other components.
+        /// </summary>
+        int PostUpdateOrder { get; }
+    }
+
+    public readonly struct ComponentWaitForCondition
+    {
+        public WaitForCondition Condition { get; }
+
+        public bool ShouldContinue { get; }
+
+        // Do not use this!
+        public ComponentWaitForCondition(WaitForCondition waitForCondition, bool shouldContinue)
+        {
+            Condition = waitForCondition;
+            ShouldContinue = shouldContinue;
+        }
+
+        public static ComponentWaitForCondition WaitForLogicTicks(int logicTicks, bool shouldContinue)
+        {
+            return new ComponentWaitForCondition(WaitForCondition.WaitForLogicTicks(logicTicks), shouldContinue);
+        }
+
+        public static ComponentWaitForCondition WaitForRobotStatus(RobotStatus status, bool shouldContinue)
+        {
+            return new ComponentWaitForCondition(WaitForCondition.WaitForRobotStatus(status), shouldContinue);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs.meta b/Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs.meta
new file mode 100644
index 000000000..4c9ad7161
--- /dev/null
+++ b/Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 3695bf108394f926194deab725789dc7
\ No newline at end of file
diff --git a/Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs b/Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs
index 5a45ebf15..abe94cd2d 100644
--- a/Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs
+++ b/Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs
@@ -1,6 +1,8 @@
 using System.Linq;
 
+using Maes.Algorithms.Patrolling.Components;
 using Maes.Map;
+using Maes.Robot;
 
 namespace Maes.Algorithms.Patrolling
 {
@@ -8,11 +10,21 @@ namespace Maes.Algorithms.Patrolling
     /// Original implementation of the Conscientious Reactive Algorithm of https://doi.org/10.1007/3-540-36483-8_11.
     /// Pseudocode can be found in another paper: https://doi.org/10.1080/01691864.2013.763722
     /// </summary>
-    public class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
+    public sealed class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
     {
         public override string AlgorithmName => "Conscientious Reactive Algorithm";
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
+        protected override IComponent[] CreateComponents(Robot2DController controller, PatrollingMap patrollingMap)
+        {
+            _goToNextVertexComponent = new GoToNextVertexComponent(NextVertex, this, controller, patrollingMap);
+
+            return new IComponent[] { _goToNextVertexComponent };
+        }
+
+        private static Vertex NextVertex(Vertex currentVertex)
         {
             return currentVertex.Neighbors.OrderBy(x => x.LastTimeVisitedTick).First();
         }
diff --git a/Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs b/Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs
index 8367bb962..5755e6266 100644
--- a/Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs
+++ b/Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs
@@ -27,7 +27,9 @@
 using System.Linq;
 using System.Text;
 
+using Maes.Algorithms.Patrolling.Components;
 using Maes.Map;
+using Maes.Robot;
 
 using UnityEngine;
 
@@ -36,15 +38,25 @@ namespace Maes.Algorithms.Patrolling
     /// <summary>
     /// Original implementation of the Heuristic Conscientious Reactive Algorithm of https://repositorio.ufpe.br/handle/123456789/2474.
     /// Pseudocode can be found in another paper: https://doi.org/10.1080/01691864.2013.763722
-    // Heuristic: The vertex with the lowest idleness and distance estimation
+    /// Heuristic: The vertex with the lowest idleness and distance estimation
     /// </summary>
-    public class HeuristicConscientiousReactiveAlgorithm : PatrollingAlgorithm
+    public sealed class HeuristicConscientiousReactiveAlgorithm : PatrollingAlgorithm
     {
         public override string AlgorithmName => "Heuristic Conscientious Reactive Algorithm";
 
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
+        protected override IComponent[] CreateComponents(Robot2DController controller, PatrollingMap patrollingMap)
+        {
+            _goToNextVertexComponent = new GoToNextVertexComponent(NextVertex, this, controller, patrollingMap);
+
+            return new IComponent[] { _goToNextVertexComponent };
+        }
+
         public delegate float DistanceEstimator(Vector2Int position);
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        private Vertex NextVertex(Vertex currentVertex)
         {
             Debug.Log($"Current vertex {currentVertex.Id}");
             // Calculate the normalized idleness of the neighbors
diff --git a/Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs b/Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs
index 192b1850f..d4bf96332 100644
--- a/Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs
+++ b/Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs
@@ -3,12 +3,9 @@
 using System.Linq;
 using System.Text;
 
+using Maes.Algorithms.Patrolling.Components;
 using Maes.Map;
-using Maes.Map.PathFinding;
 using Maes.Robot;
-using Maes.Robot.Task;
-
-using UnityEngine;
 
 namespace Maes.Algorithms.Patrolling
 {
@@ -16,33 +13,14 @@ public abstract class PatrollingAlgorithm : IPatrollingAlgorithm
     {
         public abstract string AlgorithmName { get; }
 
+        /// <inheritdoc/>
+        public Vertex TargetVertex { get; private set; } = null!;
 
         // Do not change visibility of this!
         private PatrollingMap _globalMap = null!;
 
-        public Vertex TargetVertex
-        {
-            get => _targetVertex ?? throw new InvalidOperationException("TargetVertex is null");
-            private set
-            {
-#if DEBUG
-                if (!AllowForeignVertices && !_vertices.Contains(value))
-                {
-                    throw new ArgumentException("TargetVertex is not from our patrolling map", nameof(value));
-                }
-#endif
-
-                _targetVertex = value;
-            }
-        }
-
-        private Vertex? _targetVertex;
-
         // Set by SetPatrollingMap
-        protected Vertex[] _vertices = null!;
-        private IReadOnlyDictionary<(int, int), PathStep[]> _paths = null!;
-
-        private Queue<PathStep> _currentPath = new();
+        private PatrollingMap _patrollingMap = null!;
 
         // Set by SetController
         protected Robot2DController _controller = null!;
@@ -59,17 +37,56 @@ private set
 
         private readonly StringBuilder _stringBuilder = new();
 
+        private IEnumerator<ComponentWaitForCondition>[] _componentPreUpdates = null!;
+        private IEnumerator<ComponentWaitForCondition>[] _componentPostUpdates = null!;
+
+        private readonly Dictionary<IEnumerator<ComponentWaitForCondition>, ComponentWaitForConditionState> _componentPreUpdateStates = new();
+        private readonly Dictionary<IEnumerator<ComponentWaitForCondition>, ComponentWaitForConditionState> _componentPostUpdateStates = new();
+
         protected event OnReachVertex? OnReachVertexHandler;
 
+        protected abstract IComponent[] CreateComponents(Robot2DController controller, PatrollingMap patrollingMap);
+
+        private void SetComponents(IComponent[] components)
+        {
+            var preUpdateSortedComponents = components.OrderBy(component => component.PreUpdateOrder).Select(component => component.PreUpdateLogic().GetEnumerator()).ToArray();
+            var postUpdateSortedComponents = components.OrderBy(component => component.PostUpdateOrder).Select(component => component.PostUpdateLogic().GetEnumerator()).ToArray();
+
+            _componentPreUpdates = preUpdateSortedComponents;
+            _componentPostUpdates = postUpdateSortedComponents;
+
+            foreach (var component in _componentPreUpdates)
+            {
+                _componentPreUpdateStates.Add(component, new ComponentWaitForConditionState());
+            }
+
+            foreach (var component in _componentPostUpdates)
+            {
+                _componentPostUpdateStates.Add(component, new ComponentWaitForConditionState());
+            }
+        }
+
         public void SetController(Robot2DController controller)
         {
             _controller = controller;
+
+            if (_patrollingMap != null)
+            {
+                SetComponents(CreateComponents(_controller, _patrollingMap));
+            }
         }
 
         public void SetPatrollingMap(PatrollingMap map)
         {
-            _vertices = map.Vertices;
-            _paths = map.Paths;
+            _patrollingMap = map;
+
+            // Just to ensure we get no null reference exceptions.
+            TargetVertex = map.Vertices[0];
+
+            if (_controller != null)
+            {
+                SetComponents(CreateComponents(_controller, _patrollingMap));
+            }
         }
 
         /// <inheritdoc/>
@@ -83,9 +100,10 @@ public void SubscribeOnReachVertex(OnReachVertex onReachVertex)
             OnReachVertexHandler += onReachVertex;
         }
 
-        private void OnReachTargetVertex(Vertex vertex)
+        public void OnReachTargetVertex(Vertex vertex, Vertex nextVertex)
         {
             var atTick = _controller.GetRobot().Simulation.SimulatedLogicTicks;
+            TargetVertex = nextVertex;
             OnReachVertexHandler?.Invoke(vertex.Id, atTick);
 
             if (!AllowForeignVertices || (AllowForeignVertices && !_globalMap.Vertices.Contains(vertex)))
@@ -94,146 +112,112 @@ private void OnReachTargetVertex(Vertex vertex)
             }
         }
 
-        private bool HasReachedTarget()
-        {
-            var currentPosition = _controller.SlamMap.CoarseMap.GetCurrentPosition(dependOnBrokenBehavior: false);
-            return currentPosition == TargetVertex.Position;
-        }
-
         public IEnumerable<WaitForCondition> PreUpdateLogic()
         {
             while (true)
             {
-                if (_targetVertex != null && _controller.IsCurrentlyColliding)
+                // Run components' PreUpdateLogic
+                // Stops if one has ShouldContinue = false
+                var shouldContinue = true;
+                foreach (var component in _componentPreUpdates)
                 {
-                    _controller.StopCurrentTask();
-                    yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
-
-                    _controller.Move(1.0f, reverse: true);
-                    yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
-
-                    if (_controller.IsCurrentlyColliding)
+                    shouldContinue = HandleComponent(component, _componentPreUpdateStates[component]);
+                    if (!shouldContinue)
                     {
-                        _controller.Move(1.0f, reverse: false);
-                        yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
+                        break;
                     }
-
-                    while (!HasReachedTarget())
-                    {
-                        _controller.PathAndMoveTo(TargetVertex.Position, dependOnBrokenBehaviour: false);
-                        yield return WaitForCondition.WaitForLogicTicks(1);
-                    }
-
-                    // Invalidate the old path.
-                    _currentPath.Clear();
                 }
 
-                yield return WaitForCondition.ContinueUpdateLogic();
-            }
-        }
-
-        public virtual IEnumerable<WaitForCondition> UpdateLogic()
-        {
-            TargetVertex = GetClosestVertex();
-            while (!HasReachedTarget())
-            {
-                // Do normal astar pathing
-                _controller.PathAndMoveTo(TargetVertex.Position, dependOnBrokenBehaviour: false);
-
-                yield return WaitForCondition.WaitForLogicTicks(1);
-            }
-
-            while (true)
-            {
-                SetNextVertex();
-
-                // Go to the start of the first step.
-                var initialPathStep = _currentPath.Peek();
-                foreach (var condition in MoveToPosition(initialPathStep.Start))
+                // If we should continue run UpdateLogic
+                // Otherwise don't run it and run PreUpdateLogic after 1 tick
+                if (shouldContinue)
                 {
-                    yield return condition;
+                    yield return WaitForCondition.ContinueUpdateLogic();
                 }
-
-                // Go to the end of each path step.
-                while (_currentPath.Count > 0)
+                else
                 {
-                    var target = _currentPath.Dequeue();
-                    foreach (var condition in MoveToPosition(target.End))
-                    {
-                        yield return condition;
-                    }
+                    yield return WaitForCondition.WaitForLogicTicks(1);
                 }
             }
         }
 
-        private const float _closeness = 0.25f;
-
-        private IEnumerable<WaitForCondition> MoveToPosition(Vector2Int target)
+        public IEnumerable<WaitForCondition> UpdateLogic()
         {
             while (true)
             {
-                yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
-
-                var relativePosition = GetRelativePositionTo(target);
-                if (relativePosition.Distance <= _closeness)
+                // Run components' PostUpdateLogic
+                // Stops if one has ShouldContinue = false
+                foreach (var component in _componentPostUpdates)
                 {
-                    yield break;
+                    var shouldContinue = HandleComponent(component, _componentPostUpdateStates[component]);
+                    if (!shouldContinue)
+                    {
+                        break;
+                    }
                 }
 
-                if (Math.Abs(relativePosition.RelativeAngle) > 1.5f)
-                {
-                    _controller.Rotate(relativePosition.RelativeAngle);
-                }
-                else
-                {
-                    _controller.Move(relativePosition.Distance);
-                }
+                yield return WaitForCondition.WaitForLogicTicks(1);
             }
         }
 
-        private RelativePosition GetRelativePositionTo(Vector2Int position)
+        private bool HandleComponent(IEnumerator<ComponentWaitForCondition> updateMethod,
+            ComponentWaitForConditionState componentWaitForConditionState)
         {
-            return _controller.SlamMap.CoarseMap.GetTileCenterRelativePosition(position, dependOnBrokenBehaviour: false);
+            // Handle the current state
+            switch (componentWaitForConditionState.ComponentWaitForCondition.Condition.Type)
+            {
+                case WaitForCondition.ConditionType.LogicTicks:
+                    if (--componentWaitForConditionState.LogicTicksToWaitFor == 0)
+                    {
+                        UpdateComponent(updateMethod, componentWaitForConditionState);
+                    }
+                    break;
+                case WaitForCondition.ConditionType.RobotStatus:
+                    if (componentWaitForConditionState.ComponentWaitForCondition.Condition.RobotStatus ==
+                        _controller.GetStatus())
+                    {
+                        UpdateComponent(updateMethod, componentWaitForConditionState);
+                    }
+                    break;
+                // Should only be this one in the beginning
+                // Indicates that the component should run.
+                case WaitForCondition.ConditionType.ContinueUpdateLogic:
+                    UpdateComponent(updateMethod, componentWaitForConditionState);
+                    break;
+            }
+
+            return componentWaitForConditionState.ComponentWaitForCondition.ShouldContinue;
         }
 
-        private void SetNextVertex()
+        private static void UpdateComponent(IEnumerator<ComponentWaitForCondition> updateMethod, ComponentWaitForConditionState componentWaitForConditionState)
         {
-            var currentVertex = TargetVertex;
-            OnReachTargetVertex(currentVertex);
-            TargetVertex = NextVertex(currentVertex);
-            _currentPath = new Queue<PathStep>(_paths[(currentVertex.Id, TargetVertex.Id)]);
-        }
+            updateMethod.MoveNext();
+            componentWaitForConditionState.ComponentWaitForCondition = updateMethod.Current;
 
-        protected abstract Vertex NextVertex(Vertex currentVertex);
+            if (componentWaitForConditionState.ComponentWaitForCondition.Condition.Type ==
+                WaitForCondition.ConditionType.LogicTicks)
+            {
+                componentWaitForConditionState.LogicTicksToWaitFor = componentWaitForConditionState
+                    .ComponentWaitForCondition.Condition.LogicTicks;
+            }
+        }
 
         public string GetDebugInfo()
         {
             _stringBuilder.Clear();
             _stringBuilder
-                .AppendLine(AlgorithmName)
-                .AppendFormat("Target vertex: {0}\n", TargetVertex);
+                .AppendLine(AlgorithmName);
             GetDebugInfo(_stringBuilder);
             return _stringBuilder.ToString();
         }
 
         protected virtual void GetDebugInfo(StringBuilder stringBuilder) { }
 
-        private Vertex GetClosestVertex()
+        private sealed class ComponentWaitForConditionState
         {
-            var position = _controller.GetSlamMap().GetCoarseMap().GetCurrentPosition(dependOnBrokenBehavior: false);
-            var closestVertex = _vertices[0];
-            var closestDistance = Vector2Int.Distance(position, closestVertex.Position);
-            foreach (var vertex in _vertices.AsSpan(1))
-            {
-                var distance = Vector2Int.Distance(position, vertex.Position);
-                if (distance < closestDistance)
-                {
-                    closestDistance = distance;
-                    closestVertex = vertex;
-                }
-            }
+            public ComponentWaitForCondition ComponentWaitForCondition = new(WaitForCondition.ContinueUpdateLogic(), shouldContinue: true);
 
-            return closestVertex;
+            public int LogicTicksToWaitFor = 0;
         }
     }
 }
\ No newline at end of file
diff --git a/Assets/Scripts/Algorithms/Patrolling/RandomReactive.cs b/Assets/Scripts/Algorithms/Patrolling/RandomReactive.cs
index db9c6e3f4..af9ab0446 100644
--- a/Assets/Scripts/Algorithms/Patrolling/RandomReactive.cs
+++ b/Assets/Scripts/Algorithms/Patrolling/RandomReactive.cs
@@ -1,6 +1,8 @@
 using System.Linq;
 
+using Maes.Algorithms.Patrolling.Components;
 using Maes.Map;
+using Maes.Robot;
 
 using Random = System.Random;
 
@@ -10,7 +12,7 @@ namespace Maes.Algorithms.Patrolling
     /// Original implementation of the Conscientious Reactive Algorithm of https://doi.org/10.1007/3-540-36483-8_11.
     /// Pseudocode can be found in another paper: https://doi.org/10.1080/01691864.2013.763722
     /// </summary>
-    public class RandomReactive : PatrollingAlgorithm
+    public sealed class RandomReactive : PatrollingAlgorithm
     {
         private readonly Random _random;
 
@@ -21,7 +23,17 @@ public RandomReactive(int seed)
 
         public override string AlgorithmName => "Random Reactive Algorithm";
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
+        protected override IComponent[] CreateComponents(Robot2DController controller, PatrollingMap patrollingMap)
+        {
+            _goToNextVertexComponent = new GoToNextVertexComponent(NextVertex, this, controller, patrollingMap);
+
+            return new IComponent[] { _goToNextVertexComponent };
+        }
+
+        private Vertex NextVertex(Vertex currentVertex)
         {
             var index = _random.Next(currentVertex.Neighbors.Count);
             return currentVertex.Neighbors.ElementAt(index);


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 4**

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between c2090ccc2d9066d5184e68fe9bd7c933606831ec and cbeba8db2c4aa0701c506016e3fed024ba3d373b.

</details>

<details>
<summary>📒 Files selected for processing (12)</summary>

* `Assets/Scripts/Algorithms/IPatrollingAlgorithm.cs` (0 hunks)
* `Assets/Scripts/Algorithms/Patrolling/CognitiveCoordinated.cs` (3 hunks)
* `Assets/Scripts/Algorithms/Patrolling/Components.meta` (1 hunks)
* `Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs` (1 hunks)
* `Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs.meta` (1 hunks)
* `Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs` (1 hunks)
* `Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs.meta` (1 hunks)
* `Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs` (1 hunks)
* `Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs` (2 hunks)
* `Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs` (4 hunks)
* `Assets/Scripts/Algorithms/Patrolling/RandomReactive.cs` (3 hunks)
* `Assets/Scripts/Map/Visualization/Patrolling/PatrollingTargetWaypointVisualizationMode.cs` (1 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* Assets/Scripts/Algorithms/IPatrollingAlgorithm.cs

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: Run all tests ✨
* GitHub Check: Lint

</details>

<details>
<summary>🔇 Additional comments (40)</summary><blockquote>

<details>
<summary>Assets/Scripts/Map/Visualization/Patrolling/PatrollingTargetWaypointVisualizationMode.cs (1)</summary>

`18-22`: **Address the TODO by updating the target waypoint retrieval method**

The visualization code has been commented out due to the removal of the `TargetVertex` property from the `IPatrollingAlgorithm` interface. Since the PR is moving toward a component-based architecture, you'll need to find a new way to access the target vertex information.


Consider one of these approaches:
1. Add a method to the `IPatrollingAlgorithm` interface that returns the current target vertex
2. Implement a new component (like a `TargetVertexProvider`) that can be queried
3. Use an event system where components publish their target vertices when they change

Could you clarify which approach aligns with your component-based design?

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs.meta (1)</summary>

`1-2`: **Meta file added for new component interface.**

The metadata file for the IComponent interface is correctly configured with a unique GUID.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs.meta (1)</summary>

`1-2`: **Meta file added for the new component implementation.**

The metadata file for GoToNextVertexComponent is properly configured with a unique GUID.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/Components.meta (1)</summary>

`1-8`: **Meta file for new Components directory is correctly structured.**

The folder asset metadata is properly set up with the necessary Unity configurations.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/RandomReactive.cs (6)</summary>

`3-3`: **Added necessary namespace import.**

The import for the Components namespace is correctly added to support the new component-based architecture.

---

`5-5`: **Robot import is properly referenced.**

The Robot namespace import is correctly included to support the controller parameter in the CreateComponents method.

---

`15-15`: **Class is now properly sealed.**

Making the class sealed is a good practice since this implementation is not meant to be extended.

---

`26-28`: **Component field is correctly initialized with null assertion.**

The GoToNextVertexComponent field is appropriately marked with null! since it will be initialized in CreateComponents.

---

`29-34`: **CreateComponents method properly initializes the component.**

The component creation and registration looks good. The NextVertex method is correctly passed as a delegate to the component.

---

`36-36`: **NextVertex method is now appropriately private.**

The NextVertex method's access modifier has been changed to private, which improves encapsulation as it's now only used internally.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs (6)</summary>

`30-30`: **Good inclusion of component namespace.**

The new using statement correctly supports the component-based refactoring.

---

`41-42`: **XML documentation style is now consistent.**

The heuristic description has been converted to XML documentation format, which improves code consistency.

---

`43-43`: **Class is now sealed.**

Good decision to seal the class to prevent further inheritance.

---

`47-48`: **Field initialization comment is helpful.**

The comment clarifies when the field will be initialized, which helps with understanding the initialization flow.

---

`50-55`: **Component initialization looks good.**

The CreateComponents method properly initializes the GoToNextVertexComponent with the NextVertex method.

---

`59-59`: **Method access level appropriately restricted.**

Changing NextVertex from protected to private improves encapsulation now that the component architecture is in place.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/CognitiveCoordinated.cs (5)</summary>

`6-6`: **Necessary import for component architecture.**

The Components namespace import is required for the new component-based implementation.

---

`14-14`: **Class sealed for stability.**

Making the class sealed prevents inheritance which is appropriate for this algorithm implementation.

---

`31-32`: **Component field with initialization comment.**

Clear comment indicating when the field will be initialized.

---

`34-39`: **Component creation looks good.**

The CreateComponents method initializes the component correctly and returns it in an array.

---

`54-54`: **Method access correctly restricted.**

Changed NextVertex from protected to private, which is appropriate since it's now only used by the component.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs (5)</summary>

`3-3`: **Required namespace import.**

Correctly added Components namespace for the new component-based architecture.

---

`13-13`: **Class properly sealed.**

Making the class sealed prevents further inheritance, which is appropriate for this algorithm.

---

`17-18`: **Field initialization is documented.**

Good comment explaining when this field will be set.

---

`20-25`: **Component creation looks good.**

The CreateComponents method correctly sets up the GoToNextVertexComponent.

---

`27-27`: **NextVertex method made static.**

The NextVertex method is now private and static, which is different from the other algorithm implementations where it's just private (not static).

Is this intentional? Consider making it consistent with other algorithm implementations unless there's a specific reason for it to be static.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs (2)</summary>

`7-16`: **Well-designed component interface.**

The IComponent interface provides a clear contract for components with separate pre and post update methods.

---

`31-39`: **Good factory methods for creating wait conditions.**

The static factory methods make it easy to create instances with specific wait conditions.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs (3)</summary>

`39-47`: **Constructor injection looks solid.**
This approach clearly separates dependencies for maintainability.

---

`49-57`: **Verify null or missing vertex scenario.**
If there are no vertices or a null vertex, the loop could cause an unexpected wait or error. Ensure there's a safeguard if `GetClosestVertex()` returns null or an invalid vertex.

---

`122-146`: **Check robot idle status handling.**
If the robot never transitions to idle, the `MoveToPosition` method might remain stuck waiting. Consider a timeout or alternate condition to avoid a deadlock scenario.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs (9)</summary>

`20-20`: **Patrolling map field initialization is logical.**
Using a single `PatrollingMap _patrollingMap` is consistent with the refactor toward a unified map approach.

---

`37-41`: **Component arrays and dictionaries are well-structured.**
The use of dictionaries for component states is a concise way to track each component’s progress.

---

`45-45`: **Abstract factory method for components is flexible.**
Defining `CreateComponents` enables derived classes to supply tailored components.

---

`70-83`: **Validate order of assignments in SetController/SetPatrollingMap.**
When both map and controller change, ensure dependencies are fully satisfied before creating components.

---

`97-97`: **OnReachTargetVertex method is straightforward.**
Invoking `OnReachVertexHandler` and marking the vertex visited is consistent with typical event-based patterns.

---

`137-154`: **PostUpdateLogic is neatly handled.**
Components are processed with a consistent pattern, and concurrency conflicts appear minimal under single-thread assumptions.

---

`156-196`: **Ensure no concurrency race conditions in HandleComponent and UpdateComponent.**
If multiple components rely on the same `Robot2DController` status, confirm concurrency is properly managed.

---

`202-202`: **Appending AlgorithmName in debug info is helpful.**
A straightforward improvement to identify the algorithm currently running.

---

`209-214`: **ComponentWaitForConditionState class is a clear container.**
This sealed class effectively encapsulates per-component state.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-05T14:22:28Z
user: login: mads256h
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T09:06:06Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 6**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs (1)</summary><blockquote>

`1-215`: _🧹 Nitpick (assertive)_

**Consider splitting the PR**  
This PR alters core architecture and multiple files, which can be overwhelming to review in a single bundle. If feasible, please consider splitting your changes for clarity. Let me know if you’d like assistance!

</blockquote></details>

</blockquote></details>

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs (1)</summary><blockquote>

`75-114`: _⚠️ Potential issue_

**Potential infinite loop if path is empty.**  
When `GetPathStepsToVertex` returns an empty queue, the `while(true)` block can loop forever without a fallback.  


```diff
+ if (path.Count == 0)
+ {
+     // Consider adding a fallback or fallback vertex selection
+     // to avoid an indefinite loop.
+     yield return ComponentWaitForCondition.WaitForLogicTicks(1, shouldContinue: false);
+     continue;
+ }
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between c2090ccc2d9066d5184e68fe9bd7c933606831ec and c110ac4d5e74584a8e9fbfe150646304a0ae3301.

</details>

<details>
<summary>📒 Files selected for processing (12)</summary>

* `Assets/Scripts/Algorithms/IPatrollingAlgorithm.cs` (0 hunks)
* `Assets/Scripts/Algorithms/Patrolling/CognitiveCoordinated.cs` (3 hunks)
* `Assets/Scripts/Algorithms/Patrolling/Components.meta` (1 hunks)
* `Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs` (1 hunks)
* `Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs.meta` (1 hunks)
* `Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs` (1 hunks)
* `Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs.meta` (1 hunks)
* `Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs` (1 hunks)
* `Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs` (2 hunks)
* `Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs` (4 hunks)
* `Assets/Scripts/Algorithms/Patrolling/RandomReactive.cs` (3 hunks)
* `Assets/Scripts/Map/Visualization/Patrolling/PatrollingTargetWaypointVisualizationMode.cs` (1 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* Assets/Scripts/Algorithms/IPatrollingAlgorithm.cs

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: Run all tests ✨

</details>

<details>
<summary>🔇 Additional comments (40)</summary><blockquote>

<details>
<summary>Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs.meta (1)</summary>

`1-2`: **Metadata file created for IComponent.**

This is a standard Unity metadata file for the new IComponent interface. No issues here.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs.meta (1)</summary>

`1-2`: **Metadata file created for GoToNextVertexComponent.**

This is a standard Unity metadata file for the new GoToNextVertexComponent class. No issues here.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/Components.meta (1)</summary>

`1-9`: **Metadata file created for Components directory.**

This is a standard Unity metadata file for the new Components directory. No issues here.

</details>
<details>
<summary>Assets/Scripts/Map/Visualization/Patrolling/PatrollingTargetWaypointVisualizationMode.cs (1)</summary>

`18-22`: **Address the TODO comment regarding target vertex visualization.**

The target vertex visualization has been commented out due to the removal of the `TargetVertex` property from the `IPatrollingAlgorithm` interface. Given your component-based approach, you'll need to determine a new way to access the target vertex.

Consider providing a method in the `IPatrollingAlgorithm` that returns the current target vertex, or expose it through a read-only property in the base algorithm class or specific component. This seems like an important feature for visualization that shouldn't be permanently removed.

While your PR is a good step forward with the component-based architecture, could you split future PRs if they modify more parts of the system? This will make reviews more focused.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs (3)</summary>

`41-43`: **Sealing the class looks good.**

Sealing the class helps reinforce your intended design.

---

`50-55`: **Well-structured component initialization.**

Your CreateComponents method clearly separates component setup and is easy to follow.

---

`59-59`: **Narrowing the access level.**

Making NextVertex private is consistent with tighter encapsulation.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/RandomReactive.cs (4)</summary>

`3-5`: **New using statements appear consistent.**

This aligns with the switch to a component-based structure.

---

`15-15`: **Sealing the class is beneficial.**

It prevents accidental subclassing and clarifies the intended usage.

---

`29-34`: **CreateComponents method looks solid.**

Returning IComponent arrays is a neat way to handle patrolling behavior.

---

`36-36`: **Limited scope of NextVertex.**

Keeping this logic private supports better encapsulation.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/CognitiveCoordinated.cs (4)</summary>

`6-8`: **New using statements.**

These imports are a good fit with the component-focused approach.

---

`14-14`: **Class sealed for clarity.**

Sealing helps ensure the class is used exactly as intended.

---

`34-39`: **Clean CreateComponents method.**

Centralizing component creation is a clear and maintainable design choice.

---

`54-54`: **Privatizing NextVertex.**

Making NextVertex private aligns well with the component-based refactoring.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs (3)</summary>

`3-5`: **Imports look consistent with current usage.**  
No issues found here.

---

`13-13`: **Good use of the `sealed` keyword.**  
Sealing this class prevents unintended inheritance.

---

`27-27`: **Static method for next vertex selection is neat.**  
This approach keeps the logic encapsulated and accessible without requiring instance state.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs (4)</summary>

`1-16`: **License header acknowledged.**  
No concerns here, everything is correct and consistent.

---

`44-47`: **Fields are clearly defined.**  
They match constructor parameters well.

---

`60-70`: **Constructor injection is clear.**  
Dependencies are well-defined, and the usage is straightforward.

---

`153-175`: **Movement logic seems correct.**  
Using separate steps for rotation and translation is a good approach, no major issues here.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs (3)</summary>

`1-19`: **License header acknowledged.**  
No issues noted.

---

`20-44`: **Interface structure is straightforward.**  
Exposes clear properties and logical methods for pre- and post-update processes. Nicely done.

---

`46-69`: **`ComponentWaitForCondition` is well-structured.**  
The separation of `Condition` and `ShouldContinue` is logical and clean.

</details>
<details>
<summary>Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs (15)</summary>

`6-6`: **Import is straightforward.**  
No issue with adding `using Maes.Algorithms.Patrolling.Components;`.

---

`20-20`: **Centralizing the patrolling map.**  
Moving the local map reference to `_patrollingMap` makes sense for clarity.

---

`37-39`: **Enumerator arrays fit the new architecture.**  
Introducing `_componentPreUpdates` and `_componentPostUpdates` aligns with your component-based approach.

---

`40-41`: **Dictionary-based state tracking**  
Using dictionaries to maintain component states is readable and scalable.

---

`45-46`: **Abstract factory for components.**  
`CreateComponents` provides a clear extension point for subclasses.

---

`47-64`: **Repeated calls to `SetComponents` may cause reinitialization.**  
This concern remains the same as in a past review comment. Consider clearing or reusing existing dictionary entries to prevent potential memory bloat or logical duplication upon repeated calls.

---

`70-73`: **Possible repeated SetComponents calls**  
Calling `SetComponents` again here might redundantly reinitialize the component states. You may want to verify whether that’s intended or if a guard clause is needed.

---

`78-83`: **Same repeated SetComponents concern**  
As above, ensure multiple calls to `SetComponents` won’t trigger unintended reinitializations or dictionary growth.

---

`97-97`: **Public event trigger method**  
Method signature and usage look correct. No immediate issues.

---

`112-134`: **Monitor for indefinite loops**  
This loop resumes `PreUpdateLogic` and may keep deferring logic indefinitely if components repeatedly yield `ShouldContinue = false`. Same note as a previous review.

---

`137-152`: **Symmetrical PostUpdate logic**  
The logic mirrors `PreUpdateLogic`. Ensure no scenario causes indefinite looping if components never yield a true `ShouldContinue`.

---

`156-180`: **HandleComponent concurrency caution**  
Overall structure is fine, but be mindful of potential scenarios where `RobotStatus` or `LogicTicks` conditions never satisfy. This could stall updates.

---

`185-196`: **Implementation clarity**  
`UpdateComponent` method is straightforward. No additional issues spotted.

---

`202-202`: **Appending algorithm name**  
No concerns about adding `AlgorithmName` to debugging info.

---

`209-214`: **Encapsulating state**  
Defining `ComponentWaitForConditionState` as a sealed inner class is neat and keeps logic tidy.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-06T09:06:55Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-06T09:07:13Z
user: login: mads256h
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T09:08:21Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-06T09:08:47Z
user: login: mads256h
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T09:09:01Z
user: login: mads256h
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T09:09:05Z
user: login: mads256h
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T09:09:19Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-06T09:09:22Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-06T09:09:32Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-06T09:09:43Z
user: login: mads256h
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T09:10:28Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-06T09:10:51Z
user: login: Puvikaran2001
state: APPROVED
author_association: NONE
submitted_at: 2025-03-06T10:46:21Z
user: login: Casper-NS
body: Looks fine, though i think it would be better to have a central place/registry where the ordering is kept track of rather than it being hardcoded in the individual components.
But not critical for now, this should be implemented in a separate PR later.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-06T10:48:06Z

Review comments:
diff_hunk: @@ -0,0 +1,41 @@
+using System.Collections.Generic;
+
+using Maes.Robot.Task;
+
+namespace Maes.Algorithms.Patrolling.Components
+{
+    public interface IComponent
+    {
+        IEnumerable<ComponentWaitForCondition> PreUpdateLogic();
+
+        IEnumerable<ComponentWaitForCondition> PostUpdateLogic();
+
+        int PreUpdateOrder { get; }
+
+        int PostUpdateOrder { get; }
+    }
+
+    public readonly struct ComponentWaitForCondition
+    {
+        public WaitForCondition WaitForWaitForCondition { get; }
+
+        public bool ShouldContinue { get; }
+
+        // Do not use this!
+        public ComponentWaitForCondition(WaitForCondition waitForCondition, bool shouldContinue)
+        {
+            WaitForWaitForCondition = waitForCondition;
+            ShouldContinue = shouldContinue;
+        }
path: Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**ComponentWaitForCondition struct implementation.**

The struct is well-implemented with a clear private constructor pattern. However, the property name `WaitForWaitForCondition` is redundant.

Consider renaming the property to something simpler like `Condition` or `WaitCondition` to avoid the repetitive "WaitFor" prefix:

```diff
-    public WaitForCondition WaitForWaitForCondition { get; }
+    public WaitForCondition Condition { get; }
```

And update usages accordingly.


> Committable suggestion skipped: line range outside the PR's diff.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-05T14:22:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,159 @@
+// Copyright 2025 MAEPS
+// 
+// This file is part of MAEPS
+// 
+// MAEPS is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the
+// Free Software Foundation, either version 3 of the License, or (at your option)
+// any later version.
+// 
+// MAEPS is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+// Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License along
+// with MAEPS. If not, see http://www.gnu.org/licenses/.
+// 
+// Contributors: Mads Beyer Mogensen
+
+using System;
+using System.Collections.Generic;
+
+using Maes.Map;
+using Maes.Map.PathFinding;
+using Maes.Robot;
+using Maes.Robot.Task;
+
+using UnityEngine;
+
+namespace Maes.Algorithms.Patrolling.Components
+{
+    public sealed class GoToNextVertexComponent : IComponent
+    {
+        private readonly NextVertexDelegate _nextVertexDelegate;
+        private readonly PatrollingAlgorithm _patrollingAlgorithm;
+        private readonly Robot2DController _controller;
+        private readonly PatrollingMap _patrollingMap;
+
+        public delegate Vertex NextVertexDelegate(Vertex currentVertex);
+
+        public GoToNextVertexComponent(NextVertexDelegate nextVertexDelegate, PatrollingAlgorithm patrollingAlgorithm, Robot2DController controller, PatrollingMap patrollingMap)
+        {
+            _nextVertexDelegate = nextVertexDelegate;
+            _patrollingAlgorithm = patrollingAlgorithm;
+            _controller = controller;
+            _patrollingMap = patrollingMap;
+        }
+
+        public IEnumerable<ComponentWaitForCondition> PreUpdateLogic()
+        {
+            // Go to the initial vertex.
+            var vertex = GetClosestVertex();
+            while (GetRelativePositionTo(vertex.Position).Distance > Closeness)
+            {
+                _controller.PathAndMoveTo(vertex.Position, dependOnBrokenBehaviour: false);
+                yield return ComponentWaitForCondition.WaitForLogicTicks(1, shouldContinue: false);
+            }
+
+            while (true)
+            {
+                // Tell PatrollingAlgorithm that we reached the vertex
+                _patrollingAlgorithm.OnReachTargetVertex(vertex);
+
+                // Follow the path.
+                // Go to the next vertex;
+                var targetVertex = _nextVertexDelegate(vertex);
+
+                var path = GetPathStepsToVertex(vertex, targetVertex);
+                vertex = targetVertex;
+
+                // Move to the start of the path
+                foreach (var condition in MoveToPosition(path.Peek().Start))
+                {
+                    yield return condition;
+                }
+
+                // Move to the end of each path
+                foreach (var condition in MoveToPosition(path.Dequeue().End))
+                {
+                    yield return condition;
+
+                    if (path.Count == 0)
+                    {
+                        break;
+                    }
+                }
+            }
+        }
+
path: Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Prevent potential looping if path is empty.**
When `GetPathStepsToVertex` returns an empty queue, the `while(true)` block could loop indefinitely. Consider adding a fallback condition if no path is available.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-05T14:22:28Z
author_association: NONE
diff_hunk: @@ -59,17 +34,53 @@ private set
 
         private readonly StringBuilder _stringBuilder = new();
 
+        private IEnumerator<ComponentWaitForCondition>[] _componentPreUpdates = null!;
+        private IEnumerator<ComponentWaitForCondition>[] _componentPostUpdates = null!;
+
+        private readonly Dictionary<IEnumerator<ComponentWaitForCondition>, ComponentWaitForConditionState> _componentPreUpdateStates = new();
+        private readonly Dictionary<IEnumerator<ComponentWaitForCondition>, ComponentWaitForConditionState> _componentPostUpdateStates = new();
+
         protected event OnReachVertex? OnReachVertexHandler;
 
+        protected abstract IComponent[] CreateComponents(Robot2DController controller, PatrollingMap patrollingMap);
+
+        private void SetComponents(IComponent[] components)
+        {
+            var preUpdateSortedComponents = components.OrderBy(component => component.PreUpdateOrder).Select(component => component.PreUpdateLogic().GetEnumerator()).ToArray();
+            var postUpdateSortedComponents = components.OrderBy(component => component.PostUpdateOrder).Select(component => component.PostUpdateLogic().GetEnumerator()).ToArray();
+
+            _componentPreUpdates = preUpdateSortedComponents;
+            _componentPostUpdates = postUpdateSortedComponents;
+
+            foreach (var component in _componentPreUpdates)
+            {
+                _componentPreUpdateStates.Add(component, new ComponentWaitForConditionState());
+            }
+
+            foreach (var component in _componentPostUpdates)
+            {
+                _componentPostUpdateStates.Add(component, new ComponentWaitForConditionState());
+            }
+        }
path: Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider handling duplicate calls to SetComponents.**
Repeatedly calling `SetComponents` may reinitialize dictionaries. Checking for existing entries or clearing them first may help.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-05T14:22:28Z
author_association: NONE
diff_hunk: @@ -94,146 +105,112 @@ private void OnReachTargetVertex(Vertex vertex)
             }
         }
 
-        private bool HasReachedTarget()
-        {
-            var currentPosition = _controller.SlamMap.CoarseMap.GetCurrentPosition(dependOnBrokenBehavior: false);
-            return currentPosition == TargetVertex.Position;
-        }
-
         public IEnumerable<WaitForCondition> PreUpdateLogic()
         {
             while (true)
             {
-                if (_targetVertex != null && _controller.IsCurrentlyColliding)
+                // Run components' PreUpdateLogic
+                // Stops if one has ShouldContinue = false
+                var shouldContinue = true;
+                foreach (var component in _componentPreUpdates)
                 {
-                    _controller.StopCurrentTask();
-                    yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
-
-                    _controller.Move(1.0f, reverse: true);
-                    yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
-
-                    if (_controller.IsCurrentlyColliding)
+                    shouldContinue = HandleComponent(component, _componentPreUpdateStates[component]);
+                    if (!shouldContinue)
                     {
-                        _controller.Move(1.0f, reverse: false);
-                        yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
+                        break;
                     }
-
-                    while (!HasReachedTarget())
-                    {
-                        _controller.PathAndMoveTo(TargetVertex.Position, dependOnBrokenBehaviour: false);
-                        yield return WaitForCondition.WaitForLogicTicks(1);
-                    }
-
-                    // Invalidate the old path.
-                    _currentPath.Clear();
                 }
 
-                yield return WaitForCondition.ContinueUpdateLogic();
-            }
-        }
-
-        public virtual IEnumerable<WaitForCondition> UpdateLogic()
-        {
-            TargetVertex = GetClosestVertex();
-            while (!HasReachedTarget())
-            {
-                // Do normal astar pathing
-                _controller.PathAndMoveTo(TargetVertex.Position, dependOnBrokenBehaviour: false);
-
-                yield return WaitForCondition.WaitForLogicTicks(1);
-            }
-
-            while (true)
-            {
-                SetNextVertex();
-
-                // Go to the start of the first step.
-                var initialPathStep = _currentPath.Peek();
-                foreach (var condition in MoveToPosition(initialPathStep.Start))
+                // If we should continue run UpdateLogic
+                // Otherwise don't run it and run PreUpdateLogic after 1 tick
+                if (shouldContinue)
                 {
-                    yield return condition;
+                    yield return WaitForCondition.ContinueUpdateLogic();
                 }
-
-                // Go to the end of each path step.
-                while (_currentPath.Count > 0)
+                else
                 {
-                    var target = _currentPath.Dequeue();
-                    foreach (var condition in MoveToPosition(target.End))
-                    {
-                        yield return condition;
-                    }
+                    yield return WaitForCondition.WaitForLogicTicks(1);
                 }
             }
path: Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**PreUpdateLogic loop is clean but watch for indefinite loops.**
If a component yields `shouldContinue = false`, the loop re-checks after 1 tick. Confirm there's no scenario preventing progress.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-05T14:22:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,159 @@
+// Copyright 2025 MAEPS
+// 
+// This file is part of MAEPS
+// 
+// MAEPS is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the
+// Free Software Foundation, either version 3 of the License, or (at your option)
+// any later version.
+// 
+// MAEPS is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+// Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License along
+// with MAEPS. If not, see http://www.gnu.org/licenses/.
+// 
+// Contributors: Mads Beyer Mogensen
+
+using System;
+using System.Collections.Generic;
+
+using Maes.Map;
+using Maes.Map.PathFinding;
+using Maes.Robot;
+using Maes.Robot.Task;
+
+using UnityEngine;
+
+namespace Maes.Algorithms.Patrolling.Components
+{
+    public sealed class GoToNextVertexComponent : IComponent
+    {
+        private readonly NextVertexDelegate _nextVertexDelegate;
+        private readonly PatrollingAlgorithm _patrollingAlgorithm;
+        private readonly Robot2DController _controller;
+        private readonly PatrollingMap _patrollingMap;
+
+        public delegate Vertex NextVertexDelegate(Vertex currentVertex);
+
+        public GoToNextVertexComponent(NextVertexDelegate nextVertexDelegate, PatrollingAlgorithm patrollingAlgorithm, Robot2DController controller, PatrollingMap patrollingMap)
+        {
+            _nextVertexDelegate = nextVertexDelegate;
+            _patrollingAlgorithm = patrollingAlgorithm;
+            _controller = controller;
+            _patrollingMap = patrollingMap;
+        }
+
+        public IEnumerable<ComponentWaitForCondition> PreUpdateLogic()
+        {
+            // Go to the initial vertex.
+            var vertex = GetClosestVertex();
+            while (GetRelativePositionTo(vertex.Position).Distance > Closeness)
+            {
+                _controller.PathAndMoveTo(vertex.Position, dependOnBrokenBehaviour: false);
+                yield return ComponentWaitForCondition.WaitForLogicTicks(1, shouldContinue: false);
+            }
+
+            while (true)
+            {
+                // Tell PatrollingAlgorithm that we reached the vertex
+                _patrollingAlgorithm.OnReachTargetVertex(vertex);
+
+                // Follow the path.
+                // Go to the next vertex;
+                var targetVertex = _nextVertexDelegate(vertex);
+
+                var path = GetPathStepsToVertex(vertex, targetVertex);
+                vertex = targetVertex;
+
+                // Move to the start of the path
+                foreach (var condition in MoveToPosition(path.Peek().Start))
+                {
+                    yield return condition;
+                }
+
+                // Move to the end of each path
+                foreach (var condition in MoveToPosition(path.Dequeue().End))
+                {
+                    yield return condition;
+
+                    if (path.Count == 0)
+                    {
+                        break;
+                    }
+                }
+            }
+        }
+
path: Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs
user: login: mads256h
body: It wont ever be empty
created_at: 2025-03-06T09:06:06Z
author_association: MEMBER
diff_hunk: @@ -27,7 +27,9 @@
 using System.Linq;
 using System.Text;
 
+using Maes.Algorithms.Patrolling.Components;
 using Maes.Map;
+using Maes.Robot;
path: Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**A small housekeeping note.**

Hello mads256h, this PR modifies multiple files. If it feels too large, please consider splitting it into smaller chunks for simpler review. The new using statements are consistent with the updated component-based design.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-06T09:06:54Z
author_association: NONE
diff_hunk: @@ -36,15 +38,25 @@ namespace Maes.Algorithms.Patrolling
     /// <summary>
     /// Original implementation of the Heuristic Conscientious Reactive Algorithm of https://repositorio.ufpe.br/handle/123456789/2474.
     /// Pseudocode can be found in another paper: https://doi.org/10.1080/01691864.2013.763722
-    // Heuristic: The vertex with the lowest idleness and distance estimation
+    /// Heuristic: The vertex with the lowest idleness and distance estimation
     /// </summary>
-    public class HeuristicConscientiousReactiveAlgorithm : PatrollingAlgorithm
+    public sealed class HeuristicConscientiousReactiveAlgorithm : PatrollingAlgorithm
     {
         public override string AlgorithmName => "Heuristic Conscientious Reactive Algorithm";
 
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
path: Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider removing 'null!' suppression.**

You might prefer constructor injection or lazy-loading to avoid explicitly suppressing nullable warnings.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-06T09:06:54Z
author_association: NONE
diff_hunk: @@ -21,7 +23,17 @@ public RandomReactive(int seed)
 
         public override string AlgorithmName => "Random Reactive Algorithm";
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
path: Assets/Scripts/Algorithms/Patrolling/RandomReactive.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Be mindful of 'null!' usage.**

Consider a more explicit approach, like constructor injection or lazy-loading, to avoid potential pitfalls.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-06T09:06:54Z
author_association: NONE
diff_hunk: @@ -26,6 +28,16 @@ protected override void GetDebugInfo(StringBuilder stringBuilder)
                 .AppendFormat("Last Vertex: {0}\n", _currentPath.Count > 0 ? _currentPath[^1].ToString() : "None");
         }
 
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
path: Assets/Scripts/Algorithms/Patrolling/CognitiveCoordinated.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Rethink 'null!' usage.**

A different pattern, like constructor or lazy injection, might avoid explicit null suppression.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-06T09:06:54Z
author_association: NONE
diff_hunk: @@ -1,18 +1,30 @@
 using System.Linq;
 
+using Maes.Algorithms.Patrolling.Components;
 using Maes.Map;
+using Maes.Robot;
 
 namespace Maes.Algorithms.Patrolling
 {
     /// <summary>
     /// Original implementation of the Conscientious Reactive Algorithm of https://doi.org/10.1007/3-540-36483-8_11.
     /// Pseudocode can be found in another paper: https://doi.org/10.1080/01691864.2013.763722
     /// </summary>
-    public class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
+    public sealed class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
     {
         public override string AlgorithmName => "Conscientious Reactive Algorithm";
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
path: Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Clarify use of `null!`.**  
Consider adding a code comment explaining why `_goToNextVertexComponent` is allowed to be null at initialization.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-06T09:06:54Z
author_association: NONE
diff_hunk: @@ -1,18 +1,30 @@
 using System.Linq;
 
+using Maes.Algorithms.Patrolling.Components;
 using Maes.Map;
+using Maes.Robot;
 
 namespace Maes.Algorithms.Patrolling
 {
     /// <summary>
     /// Original implementation of the Conscientious Reactive Algorithm of https://doi.org/10.1007/3-540-36483-8_11.
     /// Pseudocode can be found in another paper: https://doi.org/10.1080/01691864.2013.763722
     /// </summary>
-    public class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
+    public sealed class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
     {
         public override string AlgorithmName => "Conscientious Reactive Algorithm";
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
+        protected override IComponent[] CreateComponents(Robot2DController controller, PatrollingMap patrollingMap)
+        {
+            _goToNextVertexComponent = new GoToNextVertexComponent(NextVertex, this, controller, patrollingMap);
+
+            return new IComponent[] { _goToNextVertexComponent };
+        }
path: Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Well-structured `CreateComponents` method.**  
Returning a single-component array is fine. If future extensions require multiple components, consider a collection-based approach.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-06T09:06:54Z
author_association: NONE
diff_hunk: @@ -0,0 +1,159 @@
+// Copyright 2025 MAEPS
+// 
+// This file is part of MAEPS
+// 
+// MAEPS is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the
+// Free Software Foundation, either version 3 of the License, or (at your option)
+// any later version.
+// 
+// MAEPS is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+// Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License along
+// with MAEPS. If not, see http://www.gnu.org/licenses/.
+// 
+// Contributors: Mads Beyer Mogensen
+
+using System;
+using System.Collections.Generic;
+
+using Maes.Map;
+using Maes.Map.PathFinding;
+using Maes.Robot;
+using Maes.Robot.Task;
+
+using UnityEngine;
+
+namespace Maes.Algorithms.Patrolling.Components
+{
+    public sealed class GoToNextVertexComponent : IComponent
+    {
+        private readonly NextVertexDelegate _nextVertexDelegate;
+        private readonly PatrollingAlgorithm _patrollingAlgorithm;
+        private readonly Robot2DController _controller;
+        private readonly PatrollingMap _patrollingMap;
+
+        public delegate Vertex NextVertexDelegate(Vertex currentVertex);
+
+        public GoToNextVertexComponent(NextVertexDelegate nextVertexDelegate, PatrollingAlgorithm patrollingAlgorithm, Robot2DController controller, PatrollingMap patrollingMap)
+        {
+            _nextVertexDelegate = nextVertexDelegate;
+            _patrollingAlgorithm = patrollingAlgorithm;
+            _controller = controller;
+            _patrollingMap = patrollingMap;
+        }
+
+        public IEnumerable<ComponentWaitForCondition> PreUpdateLogic()
+        {
+            // Go to the initial vertex.
+            var vertex = GetClosestVertex();
+            while (GetRelativePositionTo(vertex.Position).Distance > Closeness)
+            {
+                _controller.PathAndMoveTo(vertex.Position, dependOnBrokenBehaviour: false);
+                yield return ComponentWaitForCondition.WaitForLogicTicks(1, shouldContinue: false);
+            }
+
+            while (true)
+            {
+                // Tell PatrollingAlgorithm that we reached the vertex
+                _patrollingAlgorithm.OnReachTargetVertex(vertex);
+
+                // Follow the path.
+                // Go to the next vertex;
+                var targetVertex = _nextVertexDelegate(vertex);
+
+                var path = GetPathStepsToVertex(vertex, targetVertex);
+                vertex = targetVertex;
+
+                // Move to the start of the path
+                foreach (var condition in MoveToPosition(path.Peek().Start))
+                {
+                    yield return condition;
+                }
+
+                // Move to the end of each path
+                foreach (var condition in MoveToPosition(path.Dequeue().End))
+                {
+                    yield return condition;
+
+                    if (path.Count == 0)
+                    {
+                        break;
+                    }
+                }
+            }
+        }
+
path: Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs
user: login: coderabbitai[bot]
body: <details>
<summary>🧩 Analysis chain</summary>

🏁 Script executed:
```
#!/bin/bash
# Examine the full implementation of GoToNextVertexComponent.cs to understand the GetPathStepsToVertex method
cat Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs
```

Length of output: 6965

---



</details>

@mads256h I see you've implemented `TryDequeue` in your code which is a good approach even if the path is guaranteed to never be empty. Thanks for clarifying that the path will always contain elements based on how the patrolling map is constructed.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: mads256h
PR: cs-24-sw-9-xx/MAEPS#263
File: Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs:0-0
Timestamp: 2025-03-06T09:07:11.574Z
Learning: In the GoToNextVertexComponent class, the GetPathStepsToVertex method won't return an empty queue because the patrolling map ensures connectivity between all vertices, as confirmed by the author.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-06T09:07:13Z
author_association: NONE
diff_hunk: @@ -94,146 +105,112 @@ private void OnReachTargetVertex(Vertex vertex)
             }
         }
 
-        private bool HasReachedTarget()
-        {
-            var currentPosition = _controller.SlamMap.CoarseMap.GetCurrentPosition(dependOnBrokenBehavior: false);
-            return currentPosition == TargetVertex.Position;
-        }
-
         public IEnumerable<WaitForCondition> PreUpdateLogic()
         {
             while (true)
             {
-                if (_targetVertex != null && _controller.IsCurrentlyColliding)
+                // Run components' PreUpdateLogic
+                // Stops if one has ShouldContinue = false
+                var shouldContinue = true;
+                foreach (var component in _componentPreUpdates)
                 {
-                    _controller.StopCurrentTask();
-                    yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
-
-                    _controller.Move(1.0f, reverse: true);
-                    yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
-
-                    if (_controller.IsCurrentlyColliding)
+                    shouldContinue = HandleComponent(component, _componentPreUpdateStates[component]);
+                    if (!shouldContinue)
                     {
-                        _controller.Move(1.0f, reverse: false);
-                        yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
+                        break;
                     }
-
-                    while (!HasReachedTarget())
-                    {
-                        _controller.PathAndMoveTo(TargetVertex.Position, dependOnBrokenBehaviour: false);
-                        yield return WaitForCondition.WaitForLogicTicks(1);
-                    }
-
-                    // Invalidate the old path.
-                    _currentPath.Clear();
                 }
 
-                yield return WaitForCondition.ContinueUpdateLogic();
-            }
-        }
-
-        public virtual IEnumerable<WaitForCondition> UpdateLogic()
-        {
-            TargetVertex = GetClosestVertex();
-            while (!HasReachedTarget())
-            {
-                // Do normal astar pathing
-                _controller.PathAndMoveTo(TargetVertex.Position, dependOnBrokenBehaviour: false);
-
-                yield return WaitForCondition.WaitForLogicTicks(1);
-            }
-
-            while (true)
-            {
-                SetNextVertex();
-
-                // Go to the start of the first step.
-                var initialPathStep = _currentPath.Peek();
-                foreach (var condition in MoveToPosition(initialPathStep.Start))
+                // If we should continue run UpdateLogic
+                // Otherwise don't run it and run PreUpdateLogic after 1 tick
+                if (shouldContinue)
                 {
-                    yield return condition;
+                    yield return WaitForCondition.ContinueUpdateLogic();
                 }
-
-                // Go to the end of each path step.
-                while (_currentPath.Count > 0)
+                else
                 {
-                    var target = _currentPath.Dequeue();
-                    foreach (var condition in MoveToPosition(target.End))
-                    {
-                        yield return condition;
-                    }
+                    yield return WaitForCondition.WaitForLogicTicks(1);
                 }
             }
path: Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs
user: login: mads256h
body: It should do this. If we are waiting for a robot status idle for example, it should prevent further progress until the state has been reached.
created_at: 2025-03-06T09:08:21Z
author_association: MEMBER
diff_hunk: @@ -94,146 +105,112 @@ private void OnReachTargetVertex(Vertex vertex)
             }
         }
 
-        private bool HasReachedTarget()
-        {
-            var currentPosition = _controller.SlamMap.CoarseMap.GetCurrentPosition(dependOnBrokenBehavior: false);
-            return currentPosition == TargetVertex.Position;
-        }
-
         public IEnumerable<WaitForCondition> PreUpdateLogic()
         {
             while (true)
             {
-                if (_targetVertex != null && _controller.IsCurrentlyColliding)
+                // Run components' PreUpdateLogic
+                // Stops if one has ShouldContinue = false
+                var shouldContinue = true;
+                foreach (var component in _componentPreUpdates)
                 {
-                    _controller.StopCurrentTask();
-                    yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
-
-                    _controller.Move(1.0f, reverse: true);
-                    yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
-
-                    if (_controller.IsCurrentlyColliding)
+                    shouldContinue = HandleComponent(component, _componentPreUpdateStates[component]);
+                    if (!shouldContinue)
                     {
-                        _controller.Move(1.0f, reverse: false);
-                        yield return WaitForCondition.WaitForRobotStatus(RobotStatus.Idle);
+                        break;
                     }
-
-                    while (!HasReachedTarget())
-                    {
-                        _controller.PathAndMoveTo(TargetVertex.Position, dependOnBrokenBehaviour: false);
-                        yield return WaitForCondition.WaitForLogicTicks(1);
-                    }
-
-                    // Invalidate the old path.
-                    _currentPath.Clear();
                 }
 
-                yield return WaitForCondition.ContinueUpdateLogic();
-            }
-        }
-
-        public virtual IEnumerable<WaitForCondition> UpdateLogic()
-        {
-            TargetVertex = GetClosestVertex();
-            while (!HasReachedTarget())
-            {
-                // Do normal astar pathing
-                _controller.PathAndMoveTo(TargetVertex.Position, dependOnBrokenBehaviour: false);
-
-                yield return WaitForCondition.WaitForLogicTicks(1);
-            }
-
-            while (true)
-            {
-                SetNextVertex();
-
-                // Go to the start of the first step.
-                var initialPathStep = _currentPath.Peek();
-                foreach (var condition in MoveToPosition(initialPathStep.Start))
+                // If we should continue run UpdateLogic
+                // Otherwise don't run it and run PreUpdateLogic after 1 tick
+                if (shouldContinue)
                 {
-                    yield return condition;
+                    yield return WaitForCondition.ContinueUpdateLogic();
                 }
-
-                // Go to the end of each path step.
-                while (_currentPath.Count > 0)
+                else
                 {
-                    var target = _currentPath.Dequeue();
-                    foreach (var condition in MoveToPosition(target.End))
-                    {
-                        yield return condition;
-                    }
+                    yield return WaitForCondition.WaitForLogicTicks(1);
                 }
             }
path: Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs
user: login: coderabbitai[bot]
body: @mads256h Thank you for confirming that behavior. I understand now - the design intentionally blocks progress until specific conditions (like reaching the idle state) are met, which is an important pattern for ensuring proper sequencing in robotics control.

I notice this PR makes substantial changes to the patrolling algorithm architecture. Have you considered splitting it into smaller, more focused PRs to make the review process more manageable?

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-06T09:08:47Z
author_association: NONE
diff_hunk: @@ -36,15 +38,25 @@ namespace Maes.Algorithms.Patrolling
     /// <summary>
     /// Original implementation of the Heuristic Conscientious Reactive Algorithm of https://repositorio.ufpe.br/handle/123456789/2474.
     /// Pseudocode can be found in another paper: https://doi.org/10.1080/01691864.2013.763722
-    // Heuristic: The vertex with the lowest idleness and distance estimation
+    /// Heuristic: The vertex with the lowest idleness and distance estimation
     /// </summary>
-    public class HeuristicConscientiousReactiveAlgorithm : PatrollingAlgorithm
+    public sealed class HeuristicConscientiousReactiveAlgorithm : PatrollingAlgorithm
     {
         public override string AlgorithmName => "Heuristic Conscientious Reactive Algorithm";
 
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
path: Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs
user: login: mads256h
body: Not going to be done in this PR
created_at: 2025-03-06T09:09:01Z
author_association: MEMBER
diff_hunk: @@ -21,7 +23,17 @@ public RandomReactive(int seed)
 
         public override string AlgorithmName => "Random Reactive Algorithm";
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
path: Assets/Scripts/Algorithms/Patrolling/RandomReactive.cs
user: login: mads256h
body: Not going to be done in this PR
created_at: 2025-03-06T09:09:05Z
author_association: MEMBER
diff_hunk: @@ -1,18 +1,30 @@
 using System.Linq;
 
+using Maes.Algorithms.Patrolling.Components;
 using Maes.Map;
+using Maes.Robot;
 
 namespace Maes.Algorithms.Patrolling
 {
     /// <summary>
     /// Original implementation of the Conscientious Reactive Algorithm of https://doi.org/10.1007/3-540-36483-8_11.
     /// Pseudocode can be found in another paper: https://doi.org/10.1080/01691864.2013.763722
     /// </summary>
-    public class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
+    public sealed class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
     {
         public override string AlgorithmName => "Conscientious Reactive Algorithm";
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
path: Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs
user: login: mads256h
body: Not going to be done in this PR
created_at: 2025-03-06T09:09:19Z
author_association: MEMBER
diff_hunk: @@ -36,15 +38,25 @@ namespace Maes.Algorithms.Patrolling
     /// <summary>
     /// Original implementation of the Heuristic Conscientious Reactive Algorithm of https://repositorio.ufpe.br/handle/123456789/2474.
     /// Pseudocode can be found in another paper: https://doi.org/10.1080/01691864.2013.763722
-    // Heuristic: The vertex with the lowest idleness and distance estimation
+    /// Heuristic: The vertex with the lowest idleness and distance estimation
     /// </summary>
-    public class HeuristicConscientiousReactiveAlgorithm : PatrollingAlgorithm
+    public sealed class HeuristicConscientiousReactiveAlgorithm : PatrollingAlgorithm
     {
         public override string AlgorithmName => "Heuristic Conscientious Reactive Algorithm";
 
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
path: Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs
user: login: coderabbitai[bot]
body: @mads256h Understood, I'll respect your decision to keep this change out of the current PR's scope. It's good practice to maintain focus on the primary objectives of each PR.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-06T09:09:22Z
author_association: NONE
diff_hunk: @@ -21,7 +23,17 @@ public RandomReactive(int seed)
 
         public override string AlgorithmName => "Random Reactive Algorithm";
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
path: Assets/Scripts/Algorithms/Patrolling/RandomReactive.cs
user: login: coderabbitai[bot]
body: @mads256h Understood, I'll respect your decision to address this in a future PR. By the way, this seems like a substantial architectural change - would it make sense to split this PR if it's modifying many files to make the review process more manageable?

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-06T09:09:32Z
author_association: NONE
diff_hunk: @@ -1,18 +1,30 @@
 using System.Linq;
 
+using Maes.Algorithms.Patrolling.Components;
 using Maes.Map;
+using Maes.Robot;
 
 namespace Maes.Algorithms.Patrolling
 {
     /// <summary>
     /// Original implementation of the Conscientious Reactive Algorithm of https://doi.org/10.1007/3-540-36483-8_11.
     /// Pseudocode can be found in another paper: https://doi.org/10.1080/01691864.2013.763722
     /// </summary>
-    public class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
+    public sealed class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
     {
         public override string AlgorithmName => "Conscientious Reactive Algorithm";
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
path: Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs
user: login: coderabbitai[bot]
body: @mads256h Understood, I'll respect your decision not to include this change in the current PR. I notice this PR modifies several files related to the component-based architecture. Have you considered splitting this into smaller PRs if the changes are extensive? This could make the review process more manageable.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-06T09:09:43Z
author_association: NONE
diff_hunk: @@ -1,18 +1,30 @@
 using System.Linq;
 
+using Maes.Algorithms.Patrolling.Components;
 using Maes.Map;
+using Maes.Robot;
 
 namespace Maes.Algorithms.Patrolling
 {
     /// <summary>
     /// Original implementation of the Conscientious Reactive Algorithm of https://doi.org/10.1007/3-540-36483-8_11.
     /// Pseudocode can be found in another paper: https://doi.org/10.1080/01691864.2013.763722
     /// </summary>
-    public class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
+    public sealed class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
     {
         public override string AlgorithmName => "Conscientious Reactive Algorithm";
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
+        protected override IComponent[] CreateComponents(Robot2DController controller, PatrollingMap patrollingMap)
+        {
+            _goToNextVertexComponent = new GoToNextVertexComponent(NextVertex, this, controller, patrollingMap);
+
+            return new IComponent[] { _goToNextVertexComponent };
+        }
path: Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs
user: login: mads256h
body: Well the Array supports multiple components so I don't understand this review
created_at: 2025-03-06T09:10:27Z
author_association: MEMBER
diff_hunk: @@ -1,18 +1,30 @@
 using System.Linq;
 
+using Maes.Algorithms.Patrolling.Components;
 using Maes.Map;
+using Maes.Robot;
 
 namespace Maes.Algorithms.Patrolling
 {
     /// <summary>
     /// Original implementation of the Conscientious Reactive Algorithm of https://doi.org/10.1007/3-540-36483-8_11.
     /// Pseudocode can be found in another paper: https://doi.org/10.1080/01691864.2013.763722
     /// </summary>
-    public class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
+    public sealed class ConscientiousReactiveAlgorithm : PatrollingAlgorithm
     {
         public override string AlgorithmName => "Conscientious Reactive Algorithm";
 
-        protected override Vertex NextVertex(Vertex currentVertex)
+        // Set by CreateComponents
+        private GoToNextVertexComponent _goToNextVertexComponent = null!;
+
+        protected override IComponent[] CreateComponents(Robot2DController controller, PatrollingMap patrollingMap)
+        {
+            _goToNextVertexComponent = new GoToNextVertexComponent(NextVertex, this, controller, patrollingMap);
+
+            return new IComponent[] { _goToNextVertexComponent };
+        }
path: Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs
user: login: coderabbitai[bot]
body: @mads256h You're absolutely right, and I apologize for the confusion. The array you're using already supports multiple components perfectly well, and your implementation is correct. My previous comment was unnecessary and poorly worded - there's no need to change your current approach since the array implementation you have is already the appropriate collection-based approach for this scenario. Sorry for the confusion!

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-06T09:10:51Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-05T14:18:22Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!IMPORTANT]
> ## Review skipped
> 
> Auto reviews are disabled on this repository.
> 
> Please check the settings in the CodeRabbit UI or the `.coderabbit.yaml` file in this repository. To trigger a single review, invoke the `@coderabbitai review` command.
> 
> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.

<!-- end of auto-generated comment: skip review by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
The changes refactor the patrolling algorithms by removing the `TargetVertex` property from the interface and consolidating map management in the base class. Several algorithm classes have been modified to be sealed, include a new private field for the `GoToNextVertexComponent`, and encapsulate the vertex selection logic via a `CreateComponents` method. A new component-based architecture is introduced via the `IComponent` interface and associated classes/metadata. Additionally, visualization for the target waypoint has been commented out pending further clarification.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `Assets/Scripts/Algorithms/IPatrollingAlgorithm.cs` | Removed the `TargetVertex` property from the interface. |
| `Assets/Scripts/Algorithms/Patrolling/CognitiveCoordinated.cs`<br>`Assets/Scripts/Algorithms/Patrolling/ConscientiousReactiveAlgorithm.cs`<br>`Assets/Scripts/Algorithms/Patrolling/HeuristicConscientiousReactive.cs`<br>`Assets/Scripts/Algorithms/Patrolling/RandomReactive.cs` | Made classes sealed; added a new private field `_goToNextVertexComponent`; introduced `CreateComponents` to initialize components; changed `NextVertex` from protected to private (or private static). |
| `Assets/Scripts/Algorithms/Patrolling/PatrollingAlgorithm.cs` | Removed target vertex logic and related fields; centralized map handling; added infrastructure for component pre/post update logic, including a new state class. |
| `Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs`<br>`Assets/Scripts/Algorithms/Patrolling/Components/GoToNextVertexComponent.cs.meta` | Added new `GoToNextVertexComponent` class for managing vertex navigation using a delegate and handling movement logic. |
| `Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs`<br>`Assets/Scripts/Algorithms/Patrolling/Components/IComponent.cs.meta`<br>`Assets/Scripts/Algorithms/Patrolling/Components.meta` | Introduced the `IComponent` interface and `ComponentWaitForCondition` struct with metadata for managing update logic. |
| `Assets/Scripts/Map/Visualization/Patrolling/PatrollingTargetWaypointVisualizationMode.cs` | Commented out target waypoint visualization logic, pending further clarification. |

## Sequence Diagram(s)
```mermaid
sequenceDiagram
    participant PA as PatrollingAlgorithm
    participant GC as GoToNextVertexComponent
    participant RC as Robot2DController
    participant PM as PatrollingMap

    PA->>GC: CreateComponents(controller, map)
    GC->>PA: Invoke NextVertex delegate
    GC->>RC: MoveToPosition(target vertex)
    RC-->>GC: Movement confirmed
```

## Poem
> Code refactored, neat and tight,  
> Components now dance in a modular light.  
> Vertices chosen with a simple call,  
> In sealed classes we’ve raised the wall.  
> Change flows in steps, bold and clear –  
> A path through code, refined and dear!  
> 🚀 Happy coding, ever so sincere!

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=cs-24-sw-9-xx/MAEPS&utm_content=263):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: mads256h
created_at: 2025-03-05T14:19:00Z
body: @coderabbitai full review
author_association: MEMBER
user: login: coderabbitai[bot]
created_at: 2025-03-05T14:19:07Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>:white_check_mark: Actions performed</summary>

Full review triggered.

</details>
author_association: NONE
user: login: mads256h
created_at: 2025-03-06T09:03:37Z
body: @coderabbitai full review
author_association: MEMBER
user: login: coderabbitai[bot]
created_at: 2025-03-06T09:03:44Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>:white_check_mark: Actions performed</summary>

Full review triggered.

</details>
author_association: NONE