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

JackAshwell11 / Hades / 14553372842

19 Apr 2025 10:19PM UTC coverage: 83.936% (-0.6%) from 84.508%
14553372842

push

github

JackAshwell11
Added functionality to run multiple episodes of a trained model and evaluate its performance through graphs and other statistics. This should allow models to be properly compared against each other to determine the best performing one.

Introduced `EpisodeResults` to collate the results of processing an episode and moved `plot_graphs()`'s functionality into `train_dqn()` allowing `plot_metric()` to be used by other functions as well.

1369 of 1631 relevant lines covered (83.94%)

9789.99 hits per line

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

99.0
/src/hades_extensions/src/ecs/registry.cpp
1
// Related header
2
#include "ecs/registry.hpp"
3

4
// Custom headers
5
#include "ecs/systems/attacks.hpp"
6
#include "ecs/systems/physics.hpp"
7

8
namespace {
9
// The percentage of velocity a game object will retain after a second.
10
constexpr double DAMPING = 0.0001;
11

12
/// Convert a Chipmunk2D data pointer to a game object ID.
13
///
14
/// @param data - The Chipmunk2D data pointer to convert.
15
/// @return The game object ID.
16
auto cpDataPointerToGameObjectID(void *data) -> GameObjectID {
15✔
17
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
18
  return static_cast<GameObjectID>(reinterpret_cast<uintptr_t>(data));
15✔
19
}
20

21
/// The collision handler for checking if the player is inside a wall.
22
///
23
/// @param arbiter - The arbiter for the collision.
24
/// @param data - The registry.
25
/// @return Always true to allow the collision to continue.
26
auto player_wall_collision_handler(cpArbiter *arbiter, cpSpace * /*space*/, void *data) -> cpBool {
1✔
27
  // Get the registry and the shapes that are colliding
28
  auto *registry{static_cast<Registry *>(data)};
1✔
29
  cpShape *shape1{nullptr};
1✔
30
  cpShape *shape2{nullptr};
1✔
31
  cpArbiterGetShapes(arbiter, &shape1, &shape2);
1✔
32

33
  // Register the post-step callback to delete the player if it is inside the wall
34
  const auto player_id{cpDataPointerToGameObjectID(cpShapeGetUserData(shape1))};
1✔
35
  if (const auto wall_position{cpBodyGetPosition(
1✔
36
          *registry->get_component<KinematicComponent>(cpDataPointerToGameObjectID(cpShapeGetUserData(shape2)))->body)};
2✔
37
      cpvdist(cpBodyGetPosition(*registry->get_component<KinematicComponent>(player_id)->body), wall_position) <
1✔
38
      (SPRITE_SIZE / 2)) {
39
    cpSpaceAddPostStepCallback(
1✔
40
        registry->get_space(),
41
        [](cpSpace * /*space*/, void *game_object_id, void *registry_ptr) {
1✔
42
          static_cast<Registry *>(registry_ptr)->delete_game_object(cpDataPointerToGameObjectID(game_object_id));
1✔
43
        },
1✔
44
        cpShapeGetUserData(shape1), registry);
45
  }
46
  return cpTrue;
1✔
47
}
48
}  // namespace
49

50
Registry::Registry(const std::mt19937 &random_generator) : random_generator_{random_generator} {
172✔
51
  // Set the damping to ensure the game objects don't drift
52
  cpSpaceSetDamping(*space_, DAMPING);
172✔
53

54
  // Add the collision handlers for the bullets
55
  createCollisionHandlerFunc(GameObjectType::Player, GameObjectType::Bullet);
172✔
56
  createCollisionHandlerFunc(GameObjectType::Enemy, GameObjectType::Bullet);
172✔
57
  createCollisionHandlerFunc(GameObjectType::Wall, GameObjectType::Bullet);
172✔
58

59
  // Add the collision handler for player<->wall collisions
60
  auto *func{cpSpaceAddCollisionHandler(get_space(), static_cast<cpCollisionType>(GameObjectType::Player),
172✔
61
                                        static_cast<cpCollisionType>(GameObjectType::Wall))};
62
  func->userData = this;
172✔
63
  func->preSolveFunc = player_wall_collision_handler;
172✔
64
}
172✔
65

66
auto Registry::create_game_object(const GameObjectType game_object_type, const cpVect &position,
10,039✔
67
                                  const std::vector<std::shared_ptr<ComponentBase>> &&components) -> GameObjectID {
68
  // Add the components to the game object
69
  game_objects_[next_game_object_id_] = {};
10,039✔
70
  game_object_types_[next_game_object_id_] = game_object_type;
10,039✔
71
  game_object_ids_[game_object_type].push_back(next_game_object_id_);
10,039✔
72
  for (const auto &component : components) {
21,376✔
73
    // Check if the component already exists in the registry
74
    const auto &obj{*component};
11,337✔
75
    if (has_component(next_game_object_id_, typeid(obj))) {
11,337✔
76
      continue;
1✔
77
    }
78

79
    // Check if the component is a kinematic component. If so, add the body and shape to the space
80
    if (typeid(obj) == typeid(KinematicComponent)) {
11,336✔
81
      const auto kinematic_component = std::static_pointer_cast<KinematicComponent>(component);
9,939✔
82
      auto *const body = *kinematic_component->body;
9,939✔
83
      auto *const shape = *kinematic_component->shape;
9,939✔
84
      cpBodySetPosition(body, game_object_type == GameObjectType::Bullet ? position : grid_pos_to_pixel(position));
9,939✔
85
      cpShapeSetCollisionType(shape, static_cast<cpCollisionType>(game_object_type));
9,939✔
86
      cpShapeSetFilter(shape, {CP_NO_GROUP, static_cast<cpBitmask>(game_object_type), CP_ALL_CATEGORIES});
9,939✔
87
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
88
      cpShapeSetUserData(shape, reinterpret_cast<void *>(static_cast<uintptr_t>(next_game_object_id_)));
9,939✔
89
      cpShapeSetBody(shape, body);
9,939✔
90
      cpSpaceAddBody(*space_, body);
9,939✔
91
      cpSpaceAddShape(*space_, shape);
9,939✔
92
    }
9,939✔
93

94
    // Add the component to the registry
95
    game_objects_[next_game_object_id_][typeid(obj)] = component;
11,336✔
96
  }
97

98
  // Increment the game object ID and return the current game object ID
99
  notify<EventType::GameObjectCreation>(next_game_object_id_);
10,039✔
100
  next_game_object_id_++;
10,039✔
101
  return next_game_object_id_ - 1;
10,039✔
102
}
103

104
void Registry::delete_game_object(const GameObjectID game_object_id) {
14✔
105
  // Check if the game object is registered or not
106
  if (!game_objects_.contains(game_object_id)) {
14✔
107
    throw RegistryError("game object", game_object_id);
5✔
108
  }
109

110
  // Remove the shape and body from the space if the game object has a kinematic component
111
  if (has_component(game_object_id, typeid(KinematicComponent))) {
13✔
112
    cpSpaceRemoveShape(*space_, *get_component<KinematicComponent>(game_object_id)->shape);
6✔
113
    cpSpaceRemoveBody(*space_, *get_component<KinematicComponent>(game_object_id)->body);
6✔
114
  }
115

116
  // Notify the callbacks then delete the game object
117
  notify<EventType::GameObjectDeath>(game_object_id);
13✔
118
  std::erase(game_object_ids_[get_game_object_type(game_object_id)], game_object_id);
13✔
119
  game_objects_.erase(game_object_id);
13✔
120
  game_object_types_.erase(game_object_id);
13✔
121
}
13✔
122

123
auto Registry::get_component(const GameObjectID game_object_id, const std::type_index &component_type) const
925✔
124
    -> std::shared_ptr<ComponentBase> {
125
  // Check if the game object has the component or not
126
  if (!has_component(game_object_id, component_type)) {
925✔
127
    throw RegistryError(game_object_id, component_type);
25✔
128
  }
129

130
  // Return the specified component
131
  return game_objects_.at(game_object_id).at(component_type);
900✔
132
}
133

134
auto Registry::get_game_object_type(const GameObjectID game_object_id) const -> GameObjectType {
33✔
135
  // Check if the game object is registered or not
136
  if (!game_object_types_.contains(game_object_id)) {
33✔
137
    throw RegistryError("game object", game_object_id);
5✔
138
  }
139

140
  // Return the game object type
141
  return game_object_types_.at(game_object_id);
32✔
142
}
143

144
auto Registry::get_game_object_ids(const GameObjectType game_object_type) -> std::vector<GameObjectID> {
16✔
145
  const auto ids{game_object_ids_.find(game_object_type)};
16✔
146
  return ids != game_object_ids_.end() ? ids->second : std::vector<GameObjectID>{};
16✔
147
}
148

149
void Registry::createCollisionHandlerFunc(GameObjectType game_object_one, GameObjectType game_object_two) {
516✔
150
  auto *func{cpSpaceAddCollisionHandler(get_space(), static_cast<cpCollisionType>(game_object_one),
516✔
151
                                        static_cast<cpCollisionType>(game_object_two))};
152
  func->userData = this;
516✔
153
  func->beginFunc = [](cpArbiter *arbiter, cpSpace * /*space*/, void *data) -> cpBool {
516✔
154
    // Get the registry and the shapes that are colliding
155
    auto *registry{static_cast<Registry *>(data)};
3✔
156
    cpShape *shape1{nullptr};
3✔
157
    cpShape *shape2{nullptr};
3✔
158
    cpArbiterGetShapes(arbiter, &shape1, &shape2);
3✔
159

160
    // Get the game object IDs of the shapes
161
    auto collision_data{std::make_unique<std::pair<GameObjectID, GameObjectID>>(
162
        cpDataPointerToGameObjectID(cpShapeGetUserData(shape1)),
3✔
163
        cpDataPointerToGameObjectID(cpShapeGetUserData(shape2)))};
6✔
164

165
    // Deal damage to the first shape if it is an entity
166
    if (static_cast<GameObjectType>(cpShapeGetCollisionType(shape1)) != GameObjectType::Wall) {
3✔
167
      cpSpaceAddPostStepCallback(
2✔
168
          registry->get_space(),
169
          [](cpSpace * /*space*/, void *ids, void *registry_ptr) {
2✔
170
            const auto *collision_ids{static_cast<std::pair<GameObjectID, GameObjectID> *>(ids)};
2✔
171
            if (const auto *reg{static_cast<Registry *>(registry_ptr)};
4✔
172
                reg->has_component(collision_ids->second, typeid(KinematicComponent))) {
2✔
173
              reg->get_system<DamageSystem>()->deal_damage(collision_ids->first, collision_ids->second);
2✔
174
            }
175
          },
2✔
176
          collision_data.get(), registry);
2✔
177
    }
178

179
    // Register the post step callback to delete the bullet
180
    cpSpaceAddPostStepCallback(
3✔
181
        registry->get_space(),
182
        [](cpSpace * /*space*/, void *bullet_id, void *registry_ptr) {
×
183
          if (auto *reg{static_cast<Registry *>(registry_ptr)};
6✔
184
              reg->has_component(cpDataPointerToGameObjectID(bullet_id), typeid(KinematicComponent))) {
3✔
185
            reg->delete_game_object(cpDataPointerToGameObjectID(bullet_id));
3✔
186
          }
187
        },
3✔
188
        cpShapeGetUserData(shape2), registry);
189

190
    // Release the collision data so Chipmunk2D can take ownership of it
191
    [[maybe_unused]] auto *const ptr{collision_data.release()};
3✔
192
    return cpFalse;
3✔
193
  };
3✔
194
}
516✔
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