| 
					
				 | 
			
			
				@@ -0,0 +1,261 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "PlayingState.hxx" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "GameStateManager.hxx" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <algorithm> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <cfenv> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <cmath> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <format> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <random> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+namespace { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_Point head_position(SDL_FPoint const& position) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#pragma STDC FENV_ACCESS ON 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    std::fesetround(FE_TONEAREST); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .x = static_cast<int>(std::nearbyint(position.x)), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .y = static_cast<int>(std::nearbyint(position.y)), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static bool operator==(SDL_Point const& lhs, SDL_Point const& rhs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return lhs.x==rhs.x && lhs.y==rhs.y; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static bool operator!=(SDL_Point const& lhs, SDL_Point const& rhs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return lhs.x!=rhs.x || lhs.y!=rhs.y; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+unsigned PlayingState::last_high_score_{0}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+PlayingState::PlayingState() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    :generator_{std::random_device{}()}, font_{"kenney_pixel.ttf"} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void PlayingState::on_enter(GameStateManager& gsm) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (void) gsm; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  length_ = 10u; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  std::uniform_int_distribution<int> distribution_direction{0, 3}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  direction_ = static_cast<Direction>(distribution_direction(generator_)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  place_head(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  place_target(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tail_.clear(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  speed_ = START_SPEED; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void PlayingState::on_event(GameStateManager& gsm, SDL_Event const& evt) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (evt.type==SDL_KEYUP && evt.key.keysym.scancode==SDL_SCANCODE_ESCAPE) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    gsm.push_state(GameStates::MainMenu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else if (evt.type==SDL_KEYDOWN) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    switch (evt.key.keysym.scancode) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case SDL_SCANCODE_UP: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (direction_==Direction::Left || direction_==Direction::Right) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        direction_ = Direction::Up; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case SDL_SCANCODE_DOWN: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (direction_==Direction::Left || direction_==Direction::Right) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        direction_ = Direction::Down; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case SDL_SCANCODE_LEFT: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (direction_==Direction::Up || direction_==Direction::Down) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        direction_ = Direction::Left; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case SDL_SCANCODE_RIGHT: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (direction_==Direction::Up || direction_==Direction::Down) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        direction_ = Direction::Right; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void PlayingState::update(GameStateManager& gsm, std::chrono::milliseconds delta_time) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  auto const distance = speed_*static_cast<float>(delta_time.count()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (distance>MAX_DISTANCE) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    SDL_Log("Snake would move a distance of %f. Game might have been stuck. Skipping cycle.", distance); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_FPoint new_head = head_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  switch (direction_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case Direction::Up: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    new_head.y -= distance; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case Direction::Down: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    new_head.y += distance; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case Direction::Left: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    new_head.x -= distance; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  case Direction::Right: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    new_head.x += distance; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  auto const old_pos = ::head_position(head_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  auto const new_pos = ::head_position(new_head); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (old_pos!=new_pos) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (new_pos==target_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ++length_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      speed_ *= ACCELERATION; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      place_target(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (detect_death(new_pos)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      last_high_score_ = length_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      gsm.replace_state(GameStates::GameOver); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    tail_.push_front(old_pos); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (tail_.size()+1>length_) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      tail_.pop_back(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  head_ = new_head; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void PlayingState::render(SDLRenderer& renderer) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int width, height; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_GetRendererOutputSize(renderer, &width, &height); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_Rect playing_field; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  double const ratio = static_cast<double>(CELLS_X)/CELLS_Y; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (width<height*ratio) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    playing_field.w = width-20; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    playing_field.h = static_cast<int>(playing_field.w/ratio); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    playing_field.h = height-70; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    playing_field.w = static_cast<int>(playing_field.h*ratio); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  playing_field.x = (width-playing_field.w)/2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  playing_field.y = 50; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_RenderClear(renderer); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  render_ui(renderer, playing_field); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  render_target(renderer, playing_field); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  render_snake(renderer, playing_field); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_RenderPresent(renderer); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void PlayingState::render_ui(SDLRenderer& renderer, SDL_Rect const& playing_field) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  TTF_Font* const font = font_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  auto const score_text = std::format("Score: {}", length_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_Surface* text_surface = TTF_RenderText_Solid(font, score_text.c_str(), {255, 255, 255, SDL_ALPHA_OPAQUE}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_Texture* text = SDL_CreateTextureFromSurface(renderer, text_surface); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_FreeSurface(text_surface); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int text_width, text_height; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_QueryTexture(text, nullptr, nullptr, &text_width, &text_height); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_Rect render_quad = {playing_field.x, 10, text_width, text_height}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_RenderCopy(renderer, text, nullptr, &render_quad); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_DestroyTexture(text); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_RenderDrawRect(renderer, &playing_field); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void PlayingState::place_target() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  target_.x = distribution_position_x_(generator_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  target_.y = distribution_position_y_(generator_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void PlayingState::place_head() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  do { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    head_.x = static_cast<float>(distribution_position_x_(generator_)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  while (head_.x<10.0f || head_.x>static_cast<float>(CELLS_X-10)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  do { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    head_.y = static_cast<float>(distribution_position_y_(generator_)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  while (head_.y<10.0f || head_.y>static_cast<float>(CELLS_Y-10)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void PlayingState::render_target(SDLRenderer& renderer, SDL_Rect const& playing_field) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  auto const ratio = playing_field.w/static_cast<double>(CELLS_X); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_Rect const target_rect{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      .x = static_cast<int>(playing_field.x+ratio*target_.x), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      .y = static_cast<int>(playing_field.y+ratio*target_.y), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      .w = static_cast<int>(ratio), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      .h = static_cast<int>(ratio), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_SetRenderDrawColor(renderer, 0, 255, 0, SDL_ALPHA_OPAQUE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_RenderFillRect(renderer, &target_rect); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void PlayingState::render_snake(SDLRenderer& renderer, SDL_Rect const& playing_field) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  auto const ratio = playing_field.w/static_cast<double>(CELLS_X); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  auto const render_dot = [ratio, playing_field, &renderer](SDL_Point const& position, double const size_factor) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    int const base_x = static_cast<int>(playing_field.x+ratio*position.x); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    int const base_y = static_cast<int>(playing_field.y+ratio*position.y); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    int const size = std::max(1, static_cast<int>(ratio*size_factor)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    int const padding = (static_cast<int>(ratio)-size) >> 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    SDL_Rect const target_rect{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .x = base_x+padding, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .y = base_y+padding, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .w = size, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .h = size, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    SDL_RenderFillRect(renderer, &target_rect); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  render_dot(::head_position(head_), 1.0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  double size = 1.0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  double const decay = 1.0/static_cast<double>(tail_.size()+1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  for (auto const& particle: tail_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    size = std::max(0.0, size-decay); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    render_dot(particle, size); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+bool PlayingState::detect_death(SDL_Point const& position) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // collision with wall 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (position.x<0 || position.x>=CELLS_X || position.y<0 || position.y>=CELLS_Y) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // collision with self 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return (std::ranges::any_of(tail_, [&position](SDL_Point const& particle) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return position==particle; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  })); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+unsigned PlayingState::last_high_score() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return last_high_score_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |