• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Scala-Robotics-Simulator / PPS-22-srs / #705

20 Oct 2025 03:28PM UTC coverage: 74.986% (-2.9%) from 77.925%
#705

Pull #113

github

web-flow
Merge branch 'main' into feature/rl-controller
Pull Request #113: feat: create reinforcement learning controller

55 of 142 new or added lines in 14 files covered. (38.73%)

6 existing lines in 3 files now uncovered.

1346 of 1795 relevant lines covered (74.99%)

8.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.79
/src/main/scala/io/github/srs/controller/RLControllerModule.scala
1
package io.github.srs.controller
2

3
import cats.effect.unsafe.implicits.global
4
import io.github.srs.model.ModelModule
5
import io.github.srs.config.SimulationConfig
6
import io.github.srs.model.environment.ValidEnvironment
7
import io.github.srs.utils.random.RNG
8
import io.github.srs.model.entity.dynamicentity.DynamicEntity
9
import io.github.srs.model.entity.dynamicentity.action.Action
10
import cats.Id
11
import io.github.srs.model.Simulation.simulation
12
import io.github.srs.model.logic.RLLogicsBundle
13
import io.github.srs.model.entity.dynamicentity.sensor.SensorReadings
14
import io.github.srs.model.entity.dynamicentity.Robot
15
import io.github.srs.model.entity.dynamicentity.sensor.Sensor.senseAll
16
import io.github.srs.view.rendering.EnvironmentRenderer
17

18
object RLControllerModule:
NEW
19

×
20
  type Observations = Map[DynamicEntity, SensorReadings]
21
  type Infos = Map[DynamicEntity, String]
5✔
22

23
  /**
24
   * The resonse after each simulation step.
25
   */
26
  case class StepResponse private[RLControllerModule] (
27
      observations: Observations,
35✔
NEW
28
      rewards: Map[DynamicEntity, Double],
×
NEW
29
      terminateds: Map[DynamicEntity, Boolean],
×
NEW
30
      truncateds: Map[DynamicEntity, Boolean],
×
NEW
31
      infos: Infos,
×
NEW
32
  )
×
33

34
  /**
35
   * Controller trait defines the interface for a Reinforcement Learning controller.
36
   */
37
  trait Controller[S <: ModelModule.BaseState]:
38

39
    /**
40
     * The type of the image used for rendering the simulation on the RL client.
41
     */
42
    type Image = Array[Byte]
43

44
    /**
45
     * The initial state of the controller.
46
     */
47
    def initialState: S
48

49
    /**
50
     * The current state of the controller.
51
     */
52
    def state: S
53

54
    /**
55
     * Initializes the controller with the given simulation configuration.
56
     *
57
     * @param config
58
     *   the simulation configuration to initialize the controller.
59
     */
60
    def init(config: SimulationConfig[ValidEnvironment]): Unit
61

62
    /**
63
     * Resets the controller to its initial state using the provided random number generator.
64
     *
65
     * @param rng
66
     *   the random number generator to use for reproducibility in the next run.
67
     */
68
    def reset(rng: RNG): (Observations, Infos)
69

70
    /**
71
     * Performs a simulation step using the provided actions for each agent.
72
     *
73
     * @param actions
74
     *   a map of agents to their corresponding actions to be performed in this step.
75
     * @return
76
     *   a response containing the results of the simulation step.
77
     */
78
    def step(actions: Map[DynamicEntity, Action[Id]]): StepResponse
79

80
    /**
81
     * Renders the current state of the simulation to an image for the RL client.
82
     *
83
     * @param width
84
     *   the width of the rendered image.
85
     * @param height
86
     *   the height of the rendered image.
87
     *
88
     * @return
89
     *   an image representing the current state of the simulation.
90
     */
91
    def render(width: Int, height: Int): Image
92
  end Controller
93

94
  trait Provider[S <: ModelModule.BaseState]:
95
    val controller: Controller[S]
96

97
  type Requirements[S <: ModelModule.BaseState] = ModelModule.Provider[S]
98

99
  trait Component[S <: ModelModule.BaseState]:
NEW
100
    context: Requirements[S] =>
×
101

102
    object Controller:
103
      def apply()(using bundle: RLLogicsBundle[S]): Controller[S] = new ControllerImpl
5✔
104

9✔
105
      private class ControllerImpl(using bundle: RLLogicsBundle[S]) extends Controller[S]:
106

8✔
107
        var _initialState: S =
108
          bundle.stateLogic.createState(SimulationConfig(simulation, ValidEnvironment.empty))
4✔
109

14✔
110
        var _state: S = initialState
111

12✔
112
        override def initialState: S = _initialState
113

3✔
114
        override def state: S = _state
115

3✔
116
        override def init(config: SimulationConfig[ValidEnvironment]): Unit =
117
          _initialState = bundle.stateLogic.createState(config)
118
          _state = _initialState
7✔
119

5✔
120
        override def reset(rng: RNG): (Observations, Infos) =
121
          _state = context.model.update(state)(using _ => bundle.stateLogic.updateState(initialState, rng))
122
          state.environment.entities.collect {
13✔
123
            // TODO: will be agent
12✔
124
            case r: Robot => (r, r.senseAll[Id](state.environment), "")
125
          }.map { case (robot, readings, info) =>
37✔
126
            (robot -> readings, robot -> info)
2✔
127
          }.unzip match
128
            case (obsList, infosList) => (obsList.toMap, infosList.toMap)
4✔
129

19✔
130
        // TODO: Use agent instead of DynamicEntity
131
        override def step(actions: Map[DynamicEntity, Action[Id]]): StepResponse =
132
          _state = context.model.update(state)(using s => bundle.tickLogic.tick(s, state.dt)).unsafeRunSync()
133
          StepResponse(
16✔
134
            observations = Map.empty,
1✔
135
            rewards = Map.empty,
3✔
136
            terminateds = Map.empty,
3✔
137
            truncateds = Map.empty,
3✔
138
            infos = Map.empty,
3✔
139
          )
5✔
140

141
        override def render(width: Int, height: Int): Image =
142
          EnvironmentRenderer.renderToPNG(state.environment, width, height)
143
      end ControllerImpl
2✔
144
    end Controller
145
  end Component
146

147
  trait Interface[S <: ModelModule.BaseState] extends Provider[S] with Component[S]:
148
    self: Requirements[S] =>
149
end RLControllerModule
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc