Переглянути джерело

:sparkles: target now only spawns in free area

Felix Bytow 1 рік тому
батько
коміт
1d9f433f93
2 змінених файлів з 46 додано та 7 видалено
  1. 44 5
      game/PlayingState.cxx
  2. 2 2
      game/PlayingState.hxx

+ 44 - 5
game/PlayingState.cxx

@@ -7,6 +7,22 @@
 #include <cmath>
 #include <format>
 #include <random>
+#include <unordered_set>
+
+#include <boost/functional/hash.hpp>
+
+namespace std {
+  template<>
+  struct hash<SDL_Point> {
+    std::size_t operator()(SDL_Point const& p) const noexcept
+    {
+      std::size_t seed{0u};
+      boost::hash_combine(seed, p.x);
+      boost::hash_combine(seed, p.y);
+      return seed;
+    }
+  };
+}
 
 namespace {
   SDL_Point head_position(SDL_FPoint const& position)
@@ -100,7 +116,10 @@ void PlayingState::update(GameStateManager& gsm, std::chrono::milliseconds const
     if (new_pos==target_) {
       ++length_;
       speed_ = std::min(MAX_SPEED, speed_*ACCELERATION);
-      place_target();
+      if (!place_target()) {
+        // technically the player finished the game at this point
+        gsm.replace_state(GameStates::GameOver);
+      }
     }
 
     if (detect_death(new_pos)) {
@@ -164,10 +183,30 @@ void PlayingState::render_ui(SDLRenderer& renderer, SDL_Rect const& playing_fiel
   SDL_RenderDrawRect(renderer, &playing_field);
 }
 
-void PlayingState::place_target()
+bool PlayingState::place_target()
 {
-  target_.x = distribution_position_x_(generator_);
-  target_.y = distribution_position_y_(generator_);
+  std::unordered_set<SDL_Point> field;
+  field.reserve(CELLS_X*CELLS_Y);
+  for (int x = 0; x<CELLS_X; ++x) {
+    for (int y = 0; y<CELLS_Y; ++y) {
+      field.insert({x, y});
+    }
+  }
+
+  for (auto const& particle: tail_) {
+    field.erase(particle);
+  }
+
+  if (field.empty()) {
+    return false;
+  }
+
+  std::vector<SDL_Point> result;
+  std::ranges::sample(field, std::back_inserter(result), 1, generator_);
+
+  target_ = result[0];
+
+  return true;
 }
 
 void PlayingState::place_head()
@@ -263,9 +302,9 @@ void PlayingState::render_game(SDLRenderer& renderer, bool is_current_state)
     SDL_RenderClear(renderer);
   }
 
-  render_ui(renderer, playing_field);
   render_snake(renderer, playing_field);
   render_target(renderer, playing_field);
+  render_ui(renderer, playing_field);
 
   if (is_current_state)
     SDL_RenderPresent(renderer);

+ 2 - 2
game/PlayingState.hxx

@@ -42,7 +42,7 @@ private:
 
   void place_head();
 
-  void place_target();
+  bool place_target();
 
   void render_ui(SDLRenderer& renderer, SDL_Rect const& playing_field);
 
@@ -54,7 +54,7 @@ private:
 
   void handle_direction_change();
 
-  std::default_random_engine generator_;
+  std::mt19937 generator_;
   std::uniform_int_distribution<int> distribution_position_x_{0, CELLS_X-1};
   std::uniform_int_distribution<int> distribution_position_y_{0, CELLS_Y-1};