diff --git a/src/Entity.h b/src/Entity.h index 3e7ffd6..de9f3b4 100644 --- a/src/Entity.h +++ b/src/Entity.h @@ -5,15 +5,20 @@ #include #include +struct AssetPair { + std::vector image; + std::vector mask; +}; + class Entity { protected: float x; float y; public: - Entity() : x(0), y(0) {} - virtual ~Entity() {} + Entity() : x(0.0f), y(0.0f) {} + virtual ~Entity() = default; - inline float getX() const { return x; } - inline float getY() const { return y; } + float getX() const noexcept { return x; } + float getY() const noexcept { return y; } }; diff --git a/src/Fish.cpp b/src/Fish.cpp index 46142dc..8a77d65 100644 --- a/src/Fish.cpp +++ b/src/Fish.cpp @@ -4,81 +4,98 @@ #include "assets/FishAssets.h" #include -Fish::Fish() : Fish(getRandomFishAsset()) {} +std::unordered_map Fish::color_map; -Fish::Fish(const FishAssetRef &pair) - : Entity(), - speed((pair.index % 2 == 0) ? Random::floatInRange(0.25, 2.25) - : -Random::floatInRange(0.25, 2.25)), - image(pair.asset.image), mask(pair.asset.mask) { - y = Random::intInRange(image.size() + 6, - Aquarium::getInstance().getHeight() - image.size()); +Fish::Fish() : Fish(getRandomAssetIndex()) {} - x = (speed < 0) ? Aquarium::getInstance().getWidth() : -20; +Fish::Fish(int asset_index) + : Entity(), image(fishAssetPairs[asset_index].image), + mask(fishAssetPairs[asset_index].mask), + speed(Random::floatInRange(0.25f, 2.25f)), + moving_right(asset_index % 2 == 0) { + const auto &aquarium = Aquarium::getInstance(); + + y = Random::intInRange(static_cast(image.size()) + 6, + aquarium.getHeight() - static_cast(image.size())); + + x = moving_right ? -20.0f : static_cast(aquarium.getWidth()); randomizeMask(); } void Fish::randomizeMask() { - std::unordered_map colorMap{{'4', 'W'}}; + // Clear and rebuild color map + color_map.clear(); + color_map['4'] = 'W'; // White is always '4' + // Assign random colors to digits 1-3, 5-9 for (char digit = '1'; digit <= '9'; ++digit) { if (digit != '4') { - colorMap[digit] = - availableColors[Random::intInRange(0, availableColors.size() - 1)]; + color_map[digit] = AVAILABLE_COLORS[Random::intInRange( + 0, static_cast(AVAILABLE_COLORS.size()) - 1)]; } } + // Apply color mapping to mask for (auto &line : mask) { for (char &ch : line) { - if (auto it = colorMap.find(ch); it != colorMap.end()) { + if (auto it = color_map.find(ch); it != color_map.end()) { ch = it->second; } } } } -Fish::FishAssetRef Fish::getRandomFishAsset() { - int index = - Random::intInRange(0, static_cast(fishAssetPairs.size()) - 1); - return FishAssetRef{index, fishAssetPairs[index]}; +int Fish::getRandomAssetIndex() { + return Random::intInRange(0, static_cast(fishAssetPairs.size()) - 1); } -void Fish::update() { x += speed; } +void Fish::update() noexcept { x += moving_right ? speed : -speed; } -void Fish::draw(int layer) { - Aquarium &aq = Aquarium::getInstance(); +void Fish::draw(int layer) const { + auto &aquarium = Aquarium::getInstance(); + + // Pre-allocate strings to avoid repeated allocations + std::string current_segment; + std::string current_colors; + current_segment.reserve(32); // Reserve reasonable capacity + current_colors.reserve(32); + + const int base_x = static_cast(x); + const int base_y = static_cast(y); for (size_t i = 0; i < image.size(); ++i) { const std::string &row = image[i]; - const std::string &maskRow = (i < mask.size()) ? mask[i] : ""; + const std::string &mask_row = (i < mask.size()) ? mask[i] : ""; - int baseY = y + static_cast(i); - int cursorX = static_cast(x); - std::string currentSegment; - std::string currentColors; + int cursor_x = base_x; + current_segment.clear(); + current_colors.clear(); for (size_t j = 0; j < row.size(); ++j) { - char ch = row[j]; + const char ch = row[j]; if (ch == '?') { - if (!currentSegment.empty()) { - aq.drawToBackBuffer(baseY, cursorX, layer, currentSegment, - currentColors); - cursorX += currentSegment.size(); - currentSegment.clear(); - currentColors.clear(); + // Flush current segment if not empty + if (!current_segment.empty()) { + aquarium.drawToBackBuffer(base_y + static_cast(i), cursor_x, + layer, current_segment, current_colors); + cursor_x += static_cast(current_segment.size()); + current_segment.clear(); + current_colors.clear(); } - cursorX += 1; + ++cursor_x; // Skip transparent character continue; } - currentSegment.push_back(ch); - currentColors.push_back((j < maskRow.size()) ? maskRow[j] : 'k'); + current_segment.push_back(ch); + current_colors.push_back((j < mask_row.size()) ? mask_row[j] : 'k'); } - if (!currentSegment.empty()) { - aq.drawToBackBuffer(baseY, cursorX, layer, currentSegment, currentColors); + // Flush remaining segment + if (!current_segment.empty()) { + aquarium.drawToBackBuffer(base_y + static_cast(i), cursor_x, layer, + current_segment, current_colors); } } } diff --git a/src/Fish.h b/src/Fish.h index 6e29207..c15c176 100644 --- a/src/Fish.h +++ b/src/Fish.h @@ -5,28 +5,23 @@ class Fish : public Entity { private: - struct FishAssetRef { - int index; - FishAsset &asset; - }; - - Fish(const FishAssetRef &ref); - static bool initialized; - - const std::vector ℑ - std::vector &mask; - - static constexpr std::array availableColors = { + static constexpr std::array AVAILABLE_COLORS = { 'c', 'C', 'r', 'R', 'y', 'Y', 'b', 'B', 'g', 'G', 'm', 'M'}; + const std::vector ℑ + std::vector mask; // Copy needed for color randomization const float speed; - static FishAssetRef getRandomFishAsset(); + const bool moving_right; - static void initializeFishAssets(); + // Static color map to avoid recreation + static std::unordered_map color_map; + + explicit Fish(int asset_index); + static int getRandomAssetIndex(); void randomizeMask(); public: Fish(); - void update(); - void draw(int layer); + void update() noexcept; + void draw(int layer) const; }; diff --git a/src/assets/FishAssets.h b/src/assets/FishAssets.h index d94e32f..9bd29ec 100644 --- a/src/assets/FishAssets.h +++ b/src/assets/FishAssets.h @@ -1,14 +1,9 @@ #pragma once -#include +#include "../Entity.h" #include -struct FishAsset { - std::vector image; - std::vector mask; -}; - -inline std::vector fishAssetPairs = { +inline std::vector fishAssetPairs = { { { R"(???\)",