Compare commits

...

7 Commits

Author SHA1 Message Date
27f33d96de create bin dir 2025-05-22 22:00:00 -04:00
86b6811c44 refactor waterline 2025-05-22 21:59:57 -04:00
96b995bbd9 refactor bubble 2025-05-22 21:59:53 -04:00
d29689e1b2 refactor fish 2025-05-22 21:59:49 -04:00
27c81ebef1 cleanup 2025-05-22 21:59:45 -04:00
198fef6a6d refactor castle 2025-05-22 21:59:40 -04:00
7dc41bdb2a decouple waterline from entity 2025-05-22 21:59:15 -04:00
13 changed files with 188 additions and 187 deletions

View File

@@ -24,6 +24,7 @@ all: $(EXEC)
# Rule to link the object files into the final executable # Rule to link the object files into the final executable
$(EXEC): $(OBJECTS) $(EXEC): $(OBJECTS)
@mkdir -p $(BIN_DIR) # Make sure the bin dir exists
$(CXX) $(OBJECTS) -o $(EXEC) $(LDFLAGS) $(CXX) $(OBJECTS) -o $(EXEC) $(LDFLAGS)
# Rule to compile .cpp files into .o files # Rule to compile .cpp files into .o files

View File

@@ -91,7 +91,7 @@ void Aquarium::redraw() {
auto &bubble = *it; auto &bubble = *it;
bubble->draw(); bubble->draw();
bubble->update(); bubble->update();
if (bubble->getY() < 9) if (bubble->isOutOfWater())
it = bubbles.erase(it); it = bubbles.erase(it);
else else
++it; ++it;
@@ -137,7 +137,7 @@ void Aquarium::redraw() {
applyBackBuffer(); applyBackBuffer();
} }
void Aquarium::addBubble(float x, float y) { void Aquarium::addBubble(size_t x, size_t y) {
bubbles.emplace_back(std::make_unique<Bubble>(x, y)); bubbles.emplace_back(std::make_unique<Bubble>(x, y));
} }

View File

@@ -80,7 +80,7 @@ public:
[[nodiscard]] int getWidth() const { return width; } [[nodiscard]] int getWidth() const { return width; }
[[nodiscard]] int getHeight() const { return height; } [[nodiscard]] int getHeight() const { return height; }
void addFish(); void addFish();
void addBubble(float x, float y); void addBubble(size_t x, size_t y);
void addSeaweed(); void addSeaweed();
void addWaterline(); void addWaterline();
void addCastle(); void addCastle();

View File

@@ -2,25 +2,17 @@
#include "Aquarium.h" #include "Aquarium.h"
#include <ncurses.h> #include <ncurses.h>
Bubble::Bubble(float x, float y) : Entity() { Bubble::Bubble(size_t x, size_t y) : x(x), y(y) {}
this->x = x;
this->y = y; void Bubble::update() {
--y;
++lifetime;
} }
void Bubble::update() { y -= 1; } void Bubble::draw() const {
static const std::string colorString(1, BUBBLE_COLOR);
void Bubble::draw() { // Clamp frame index
lifetime++; int frameIndex = std::min(lifetime / FRAMES_PER_ANIMATION, MAX_FRAME_INDEX);
Aquarium::getInstance().drawToBackBuffer(y, x, 0, BUBBLE_FRAMES[frameIndex],
// Determine the frame based on lifetime colorString);
int frameNumber = lifetime / 9;
if (frameNumber > 2)
frameNumber = 2;
char frame = bubbleChars[frameNumber];
std::string line(1, frame);
std::string colorLine(1, 'c');
Aquarium::getInstance().drawToBackBuffer(y, x, 0, line, colorLine);
} }

View File

@@ -1,14 +1,21 @@
#pragma once #pragma once
#include "Entity.h" #include <cstddef>
#include <string>
class Bubble : public Entity { class Bubble {
private: private:
static constexpr char bubbleChars[3] = {'.', 'o', 'O'}; static constexpr const char *BUBBLE_FRAMES[3] = {".", "o", "O"};
static constexpr int FRAMES_PER_ANIMATION = 9;
static constexpr int MAX_FRAME_INDEX = 2;
static constexpr char BUBBLE_COLOR = 'c';
size_t x, y;
int lifetime = 0; int lifetime = 0;
public: public:
Bubble(float x, float y); Bubble(size_t x, size_t y);
bool isOutOfWater() const { return y < 5; }
void update(); void update();
void draw(); void draw() const;
}; };

View File

@@ -1,39 +1,24 @@
#include "Castle.h" #include "Castle.h"
#include "Aquarium.h" #include "Aquarium.h"
#include "assets/CastleAssets.h"
const std::vector<std::string> Castle::image = { Castle::Castle()
R"( T~~ )", R"( | )", : x(Aquarium::getInstance().getWidth() - 32),
R"( /^\ )", R"( / \ )", y(Aquarium::getInstance().getHeight() - 13), image(castleAsset.image),
R"( _ _ _ / \ _ _ _ )", R"([ ]_[ ]_[ ]/ _ _ \[ ]_[ ]_[ ])", mask(castleAsset.mask) {}
R"(|_=__-_ =_|_[ ]_[ ]_|_=-___-__|)", R"( | _- = | =_ = _ |= _= | )",
R"( |= -[] |- = _ = |_-=_[] | )", R"( | =_ |= - ___ | =_ = | )",
R"( |= []- |- /| |\ |=_ =[] | )", R"( |- =_ | =| | | | |- = - | )",
R"( |_______|__|_|_|_|__|_______| )"};
const std::vector<std::string> Castle::mask = { void Castle::draw() const {
R"( RR )", R"( )", auto &aquarium = Aquarium::getInstance();
R"( yyy )", R"( y y )",
R"( y y )", R"( y y )",
R"( )", R"( )",
R"( )", R"( yyy )",
R"( yy yy )", R"( y y y y )",
R"( yyyyyyy )"};
Castle::Castle() : Entity() {
x = Aquarium::getInstance().getWidth() - 32;
y = Aquarium::getInstance().getHeight() - 13;
}
void Castle::draw() {
for (size_t i = 0; i < image.size(); ++i) { for (size_t i = 0; i < image.size(); ++i) {
std::string currentLine; std::string currentLine;
std::string colorLine; std::string colorLine;
currentLine.reserve(image[i].size());
colorLine.reserve(image[i].size());
// Iterate over characters in the current line // Iterate over characters in the current line
for (size_t j = 0; j < image[i].size(); ++j) { for (size_t j = 0; j < image[i].size(); ++j) {
char ch = image[i][j]; char ch = image[i][j];
if (ch == '?')
continue;
char colorChar = 'K'; // default to black char colorChar = 'K'; // default to black
if (i < mask.size() && j < mask[i].size() && mask[i][j] != ' ') if (i < mask.size() && j < mask[i].size() && mask[i][j] != ' ')
@@ -44,9 +29,6 @@ void Castle::draw() {
colorLine += colorChar; colorLine += colorChar;
} }
Aquarium::getInstance().drawToBackBuffer(y + i, x, 0, currentLine, aquarium.drawToBackBuffer(y + i, x, 0, currentLine, colorLine);
colorLine);
} }
} }
void Castle::update() { return; }

View File

@@ -1,14 +1,14 @@
#pragma once #pragma once
#include "Entity.h" #include <string>
#include <vector>
class Castle : public Entity { class Castle {
private: private:
static const std::vector<std::string> image; const size_t x, y;
static const std::vector<std::string> mask; const std::vector<std::string> image;
const std::vector<std::string> mask;
public: public:
Castle(); Castle();
void draw() const;
void draw();
void update();
}; };

View File

@@ -1,16 +1,16 @@
#include "Fish.h" #include "Fish.h"
#include "Aquarium.h" #include "Aquarium.h"
#include "FishAssets.h"
#include "Random.h" #include "Random.h"
#include "assets/FishAssets.h"
#include <ncurses.h> #include <ncurses.h>
Fish::Fish() : Fish(getRandomFishPair()) {} Fish::Fish() : Fish(getRandomFishAsset()) {}
Fish::Fish(const FishAssetPair &pair) Fish::Fish(const FishAssetRef &pair)
: Entity(), : Entity(),
speed((pair.index % 2 == 0) ? Random::floatInRange(0.25, 2.25) speed((pair.index % 2 == 0) ? Random::floatInRange(0.25, 2.25)
: -Random::floatInRange(0.25, 2.25)), : -Random::floatInRange(0.25, 2.25)),
image(*pair.image), refMask(*pair.mask) { image(pair.asset.image), mask(pair.asset.mask) {
y = Random::intInRange(image.size() + 6, y = Random::intInRange(image.size() + 6,
Aquarium::getInstance().getHeight() - image.size()); Aquarium::getInstance().getHeight() - image.size());
@@ -19,50 +19,29 @@ Fish::Fish(const FishAssetPair &pair)
randomizeMask(); randomizeMask();
} }
std::vector<std::pair<std::vector<std::string>, std::vector<std::string>>>
Fish::fishPairs;
bool Fish::initialized = false;
void Fish::initializeFishAssets() {
if (initialized)
return;
fishPairs = fishAssetPairs;
initialized = true;
}
void Fish::randomizeMask() { void Fish::randomizeMask() {
// Create a mapping of digit to color std::unordered_map<char, char> colorMap{{'4', 'W'}};
std::unordered_map<char, char> colorMap;
mask = refMask;
// For each digit 1-9, assign a random color
for (char digit = '1'; digit <= '9'; ++digit) { for (char digit = '1'; digit <= '9'; ++digit) {
if (digit != '4') {
colorMap[digit] = colorMap[digit] =
availableColors[Random::intInRange(0, availableColors.size() - 1)]; availableColors[Random::intInRange(0, availableColors.size() - 1)];
} }
}
// Special case: '4' always maps to 'W'
colorMap['4'] = 'W';
// Apply the color mapping to each character in the mask
for (auto &line : mask) { for (auto &line : mask) {
for (auto &ch : line) { for (char &ch : line) {
if (ch >= '1' && ch <= '9') { if (auto it = colorMap.find(ch); it != colorMap.end()) {
ch = colorMap[ch]; ch = it->second;
} }
} }
} }
} }
Fish::FishAssetPair Fish::getRandomFishPair() { Fish::FishAssetRef Fish::getRandomFishAsset() {
if (!initialized) int index =
initializeFishAssets(); Random::intInRange(0, static_cast<int>(fishAssetPairs.size()) - 1);
return FishAssetRef{index, fishAssetPairs[index]};
int index = Random::intInRange(0, fishPairs.size() - 1);
return FishAssetPair{index, &fishPairs[index].first,
&fishPairs[index].second};
} }
void Fish::update() { x += speed; } void Fish::update() { x += speed; }

View File

@@ -1,31 +1,26 @@
#pragma once #pragma once
#include "Entity.h" #include "Entity.h"
#include "assets/FishAssets.h"
#include <array> #include <array>
class Fish : public Entity { class Fish : public Entity {
private: private:
struct FishAssetPair { struct FishAssetRef {
int index; int index;
const std::vector<std::string> *image; FishAsset &asset;
const std::vector<std::string> *mask;
}; };
Fish(const FishAssetPair &pair); Fish(const FishAssetRef &ref);
static std::vector<
std::pair<std::vector<std::string>, std::vector<std::string>>>
fishPairs;
static bool initialized; static bool initialized;
const std::vector<std::string> &image; const std::vector<std::string> &image;
const std::vector<std::string> &refMask; std::vector<std::string> &mask;
std::vector<std::string> mask;
static constexpr std::array<char, 12> availableColors = { static constexpr std::array<char, 12> availableColors = {
'c', 'C', 'r', 'R', 'y', 'Y', 'b', 'B', 'g', 'G', 'm', 'M'}; 'c', 'C', 'r', 'R', 'y', 'Y', 'b', 'B', 'g', 'G', 'm', 'M'};
const float speed; const float speed;
static FishAssetPair getRandomFishPair(); static FishAssetRef getRandomFishAsset();
static void initializeFishAssets(); static void initializeFishAssets();
void randomizeMask(); void randomizeMask();

View File

@@ -3,38 +3,38 @@
#include "Random.h" #include "Random.h"
#include "defs.h" #include "defs.h"
#include <algorithm> #include <algorithm>
#include <ncurses.h>
Waterline::Waterline() { Waterline::Waterline() : x(0), y(WATERLINE_Y) {
std::vector<std::string> baseShape = { shape = {
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", "^^^^ ^^^ ^^^ ^^^ ^^^^ ", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", "^^^^ ^^^ ^^^ ^^^ ^^^^ ",
"^^^^ ^^^^ ^^^ ^^ ", "^^ ^^^^ ^^^ ^^^^^^ "}; "^^^^ ^^^^ ^^^ ^^ ", "^^ ^^^^ ^^^ ^^^^^^ "};
int seg_len = baseShape[0].size(); const size_t width = Aquarium::getInstance().getWidth();
int repeat = Aquarium::getInstance().getWidth() / seg_len + 1; for (auto &line : shape) {
const std::string original = line;
for (auto &line : baseShape) { while (line.size() < width) {
std::string original = line;
while (line.size() < Aquarium::getInstance().getWidth()) {
line += original; line += original;
} }
colorLines.emplace_back(line.size(), WATERLINE_COLOR);
} }
shape = std::move(baseShape);
y = 5;
} }
void Waterline::draw() { void Waterline::draw() const {
for (size_t i = 0; i < shape.size(); ++i) { for (size_t i = 0; i < shape.size(); ++i) {
Aquarium::getInstance().drawToBackBuffer(i + y, x, 0, shape[i], Aquarium::getInstance().drawToBackBuffer(y + static_cast<int>(i), x, 0,
std::string(shape[i].size(), 'c')); shape[i], colorLines[i]);
} }
} }
void Waterline::update() { void Waterline::update() {
// Skip the first line (index 0) as it's static
for (size_t i = 1; i < shape.size(); ++i) { for (size_t i = 1; i < shape.size(); ++i) {
// Probability increases with line index (later lines = higher chance) // Probability increases with depth (higher index = more movement)
float chance = static_cast<float>(i) / shape.size(); float movementChance =
if (Random::floatInRange(0.0f, 1.0f) < chance * (1.0f / WAVE_MOVE_CHANCE)) { static_cast<float>(i) / static_cast<float>(shape.size());
float threshold = movementChance / WAVE_MOVE_CHANCE;
if (Random::floatInRange(0.0f, 1.0f) < threshold) {
int direction = Random::intInRange(0, 1) == 0 ? -1 : 1; int direction = Random::intInRange(0, 1) == 0 ? -1 : 1;
shiftString(shape[i], direction); shiftString(shape[i], direction);
} }
@@ -42,10 +42,7 @@ void Waterline::update() {
} }
void Waterline::shiftString(std::string &str, int direction) { void Waterline::shiftString(std::string &str, int direction) {
if (str.empty() || (direction != 1 && direction != -1)) if (direction > 0) {
return;
if (direction == 1) {
std::rotate(str.rbegin(), str.rbegin() + 1, str.rend()); std::rotate(str.rbegin(), str.rbegin() + 1, str.rend());
} else { } else {
std::rotate(str.begin(), str.begin() + 1, str.end()); std::rotate(str.begin(), str.begin() + 1, str.end());

View File

@@ -1,13 +1,21 @@
#pragma once #pragma once
#include "Entity.h" #include <string>
#include <vector>
class Waterline : public Entity { class Waterline {
private: private:
static constexpr int WATERLINE_Y = 5;
static constexpr char WATERLINE_COLOR = 'c';
size_t x, y;
std::vector<std::string> shape; std::vector<std::string> shape;
void shiftString(std::string &, int direction); std::vector<std::string> colorLines;
void shiftString(std::string &str, int direction);
void initializeShape();
public: public:
Waterline(); Waterline();
void draw(); void draw() const;
void update(); void update();
}; };

38
src/assets/CastleAssets.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include <string>
#include <vector>
struct CastleAsset {
std::vector<std::string> image;
std::vector<std::string> mask;
};
inline CastleAsset castleAsset = {{
R"( T~~)",
R"( |)",
R"( /^\)",
R"( / \)",
R"( _ _ _ / \ _ _ _)",
R"([ ]_[ ]_[ ]/ _ _ \[ ]_[ ]_[ ])",
R"(|_=__-_ =_|_[ ]_[ ]_|_=-___-__|)",
R"( | _- = | =_ = _ |= _= |)",
R"( |= -[] |- = _ = |_-=_[] |)",
R"( | =_ |= - ___ | =_ = |)",
R"( |= []- |- /| |\ |=_ =[] |)",
R"( |- =_ | =| | | | |- = - |)",
R"( |_______|__|_|_|_|__|_______|)"},
{
R"( RR)",
R"()",
R"( yyy)",
R"( y y)",
R"( y y)",
R"( y y)",
R"()",
R"()",
R"()",
R"( yyy)",
R"( yy yy)",
R"( y y y y)",
R"( yyyyyyy)"}};

View File

@@ -3,10 +3,12 @@
#include <string> #include <string>
#include <vector> #include <vector>
using FishAssetPairRaw = struct FishAsset {
std::pair<std::vector<std::string>, std::vector<std::string>>; std::vector<std::string> image;
std::vector<std::string> mask;
};
inline std::vector<FishAssetPairRaw> fishAssetPairs = { inline std::vector<FishAsset> fishAssetPairs = {
{ {
{ {
R"(???\)", R"(???\)",
@@ -81,7 +83,7 @@ inline std::vector<FishAssetPairRaw> fishAssetPairs = {
}, },
{ {
{ {
R"(???????,--,_???)", R"(???????,--,_)",
R"(__????_\.---'-.)", R"(__????_\.---'-.)",
R"(\ '.-" // o\)", R"(\ '.-" // o\)",
R"(/_.'-._ \\ /)", R"(/_.'-._ \\ /)",
@@ -97,11 +99,11 @@ inline std::vector<FishAssetPairRaw> fishAssetPairs = {
}, },
{ {
{ {
R"(????_,--,???????)", R"(????_,--,)",
R"(?.-'---./_????__)", R"(?.-'---./_????__)",
R"(/o \\ "-.' /)", R"(/o \\ "-.' /)",
R"(\ // _.-'._\)", R"(\ // _.-'._\)",
R"(?`"\)--"`???????)" R"(?`"\)--"`)"
}, },
{ {
R"( 22222)", R"( 22222)",
@@ -137,12 +139,12 @@ inline std::vector<FishAssetPairRaw> fishAssetPairs = {
}, },
{ {
{ {
R"(????????_.-`\??????)", R"(????????_.-`\)",
R"(?????-:`_..,_\?????)", R"(?????-:`_..,_\)",
R"(('-..:-` , '-.,?)", R"(('-..:-` , '-.,)",
R"(?} _ ;':( o :)", R"(?} _ ;':( o :)",
R"((.-`/'-.,__'` _.-`?)", R"((.-`/'-.,__'` _.-`)",
R"(???`'-.,/??//`?????)" R"(???`'-.,/??//`)"
}, },
{ {
R"( 22222)", R"( 22222)",
@@ -155,12 +157,12 @@ inline std::vector<FishAssetPairRaw> fishAssetPairs = {
}, },
{ {
{ {
R"(??????/`-._????????)", R"(??????/`-._)",
R"(?????/_,.._`:-?????)", R"(?????/_,.._`:-)",
R"(?,.-' , `-:..-'))", R"(?,.-' , `-:..-'))",
R"(: o ):'; _ {?)", R"(: o ):'; _ {)",
R"(?`-._ `'__,.-'\`-.))", R"(?`-._ `'__,.-'\`-.))",
R"(?????`\\??\,.-'`???)" R"(?????`\\??\,.-'`)"
}, },
{ {
R"( 22222)", R"( 22222)",
@@ -173,12 +175,12 @@ inline std::vector<FishAssetPairRaw> fishAssetPairs = {
}, },
{ {
{ {
R"(????????/\??????)", R"(????????/\)",
R"(????????\.\_????)", R"(????????\.\_)",
R"(\'-,.:-` '-,?)", R"(\'-,.:-` '-,)",
R"( ) _ (>( o <)", R"( ) _ (>( o <)",
R"(/.-`?':._ _.-`?)", R"(/.-`?':._ _.-`)",
R"(??????;/?``?????)", R"(??????;/?``)",
}, },
{ {
R"( 22)", R"( 22)",
@@ -191,12 +193,12 @@ inline std::vector<FishAssetPairRaw> fishAssetPairs = {
}, },
{ {
{ {
R"(??????/\????????)", R"(??????/\)",
R"(????_/./????????)", R"(????_/./)",
R"(?,-' `-:.,-'/)", R"(?,-' `-:.,-'/)",
R"(> o )<) _ ()", R"(> o )<) _ ()",
R"(?`-._ _.:'?`-.\)", R"(?`-._ _.:'?`-.\)",
R"(?????``?\;??????)", R"(?????``?\;)",
}, },
{ {
R"( 22)", R"( 22)",
@@ -209,10 +211,10 @@ inline std::vector<FishAssetPairRaw> fishAssetPairs = {
}, },
{ {
{ {
R"(_?????????_.*"\??????)", R"(_?????????_.*"\)",
R"(\'-._..-*` `'*-.??)", R"(\'-._..-*` `'*-.)",
R"(?) , (( o >)", R"(?) , (( o >)",
R"(/.`"*--.__)_.`_.-*`??)" R"(/.`"*--.__)_.`_.-*`)"
}, },
{ {
R"(6 11222)", R"(6 11222)",