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
$(EXEC): $(OBJECTS)
@mkdir -p $(BIN_DIR) # Make sure the bin dir exists
$(CXX) $(OBJECTS) -o $(EXEC) $(LDFLAGS)
# Rule to compile .cpp files into .o files

View File

@@ -91,7 +91,7 @@ void Aquarium::redraw() {
auto &bubble = *it;
bubble->draw();
bubble->update();
if (bubble->getY() < 9)
if (bubble->isOutOfWater())
it = bubbles.erase(it);
else
++it;
@@ -137,7 +137,7 @@ void Aquarium::redraw() {
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));
}

View File

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

View File

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

View File

@@ -1,14 +1,21 @@
#pragma once
#include "Entity.h"
#include <cstddef>
#include <string>
class Bubble : public Entity {
class Bubble {
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;
public:
Bubble(float x, float y);
Bubble(size_t x, size_t y);
bool isOutOfWater() const { return y < 5; }
void update();
void draw();
void draw() const;
};

View File

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

View File

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

View File

@@ -1,16 +1,16 @@
#include "Fish.h"
#include "Aquarium.h"
#include "FishAssets.h"
#include "Random.h"
#include "assets/FishAssets.h"
#include <ncurses.h>
Fish::Fish() : Fish(getRandomFishPair()) {}
Fish::Fish() : Fish(getRandomFishAsset()) {}
Fish::Fish(const FishAssetPair &pair)
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.image), refMask(*pair.mask) {
image(pair.asset.image), mask(pair.asset.mask) {
y = Random::intInRange(image.size() + 6,
Aquarium::getInstance().getHeight() - image.size());
@@ -19,50 +19,29 @@ Fish::Fish(const FishAssetPair &pair)
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() {
// Create a mapping of digit to color
std::unordered_map<char, char> colorMap;
mask = refMask;
std::unordered_map<char, char> colorMap{{'4', 'W'}};
// For each digit 1-9, assign a random color
for (char digit = '1'; digit <= '9'; ++digit) {
colorMap[digit] =
availableColors[Random::intInRange(0, availableColors.size() - 1)];
if (digit != '4') {
colorMap[digit] =
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 &ch : line) {
if (ch >= '1' && ch <= '9') {
ch = colorMap[ch];
for (char &ch : line) {
if (auto it = colorMap.find(ch); it != colorMap.end()) {
ch = it->second;
}
}
}
}
Fish::FishAssetPair Fish::getRandomFishPair() {
if (!initialized)
initializeFishAssets();
int index = Random::intInRange(0, fishPairs.size() - 1);
return FishAssetPair{index, &fishPairs[index].first,
&fishPairs[index].second};
Fish::FishAssetRef Fish::getRandomFishAsset() {
int index =
Random::intInRange(0, static_cast<int>(fishAssetPairs.size()) - 1);
return FishAssetRef{index, fishAssetPairs[index]};
}
void Fish::update() { x += speed; }

View File

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

View File

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

View File

@@ -1,13 +1,21 @@
#pragma once
#include "Entity.h"
#include <string>
#include <vector>
class Waterline : public Entity {
class Waterline {
private:
static constexpr int WATERLINE_Y = 5;
static constexpr char WATERLINE_COLOR = 'c';
size_t x, y;
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:
Waterline();
void draw();
void draw() const;
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 <vector>
using FishAssetPairRaw =
std::pair<std::vector<std::string>, std::vector<std::string>>;
struct FishAsset {
std::vector<std::string> image;
std::vector<std::string> mask;
};
inline std::vector<FishAssetPairRaw> fishAssetPairs = {
inline std::vector<FishAsset> fishAssetPairs = {
{
{
R"(???\)",
@@ -81,7 +83,7 @@ inline std::vector<FishAssetPairRaw> fishAssetPairs = {
},
{
{
R"(???????,--,_???)",
R"(???????,--,_)",
R"(__????_\.---'-.)",
R"(\ '.-" // o\)",
R"(/_.'-._ \\ /)",
@@ -97,18 +99,18 @@ inline std::vector<FishAssetPairRaw> fishAssetPairs = {
},
{
{
R"(????_,--,???????)",
R"(????_,--,)",
R"(?.-'---./_????__)",
R"(/o \\ "-.' /)",
R"(\ // _.-'._\)",
R"(?`"\)--"`???????)"
R"(?`"\)--"`)"
},
{
R"( 22222 )",
R"( 22222)",
R"( 112111121 66)",
R"(14 77 1116 6)",
R"(1 77 1111666)",
R"( 11331111 )"
R"( 11331111)"
}
},
{
@@ -137,101 +139,101 @@ inline std::vector<FishAssetPairRaw> fishAssetPairs = {
},
{
{
R"(????????_.-`\??????)",
R"(?????-:`_..,_\?????)",
R"(('-..:-` , '-.,?)",
R"(????????_.-`\)",
R"(?????-:`_..,_\)",
R"(('-..:-` , '-.,)",
R"(?} _ ;':( o :)",
R"((.-`/'-.,__'` _.-`?)",
R"(???`'-.,/??//`?????)"
R"((.-`/'-.,__'` _.-`)",
R"(???`'-.,/??//`)"
},
{
R"( 22222 )",
R"( 222111112 )",
R"(66661111 7 1111 )",
R"( 22222)",
R"( 222111112)",
R"(66661111 7 1111)",
R"( 6 1 7777 4 1)",
R"(6666211111177 1111 )",
R"( 222222 333 )"
R"(6666211111177 1111)",
R"( 222222 333)"
}
},
{
{
R"(??????/`-._????????)",
R"(?????/_,.._`:-?????)",
R"(??????/`-._)",
R"(?????/_,.._`:-)",
R"(?,.-' , `-:..-'))",
R"(: o ):'; _ {?)",
R"(: o ):'; _ {)",
R"(?`-._ `'__,.-'\`-.))",
R"(?????`\\??\,.-'`???)"
R"(?????`\\??\,.-'`)"
},
{
R"( 22222 )",
R"( 211111222 )",
R"( 22222)",
R"( 211111222)",
R"( 1111 7 11116666)",
R"(1 4 7777 1 6 )",
R"(1 4 7777 1 6)",
R"( 1111 7711111126666)",
R"( 333 222222 )"
R"( 333 222222)"
}
},
{
{
R"(????????/\??????)",
R"(????????\.\_????)",
R"(\'-,.:-` '-,?)",
R"(????????/\)",
R"(????????\.\_)",
R"(\'-,.:-` '-,)",
R"( ) _ (>( o <)",
R"(/.-`?':._ _.-`?)",
R"(??????;/?``?????)",
R"(/.-`?':._ _.-`)",
R"(??????;/?``)",
},
{
R"( 22 )",
R"( 2121 )",
R"(66661111 111 )",
R"( 22)",
R"( 2121)",
R"(66661111 111)",
R"( 6 1 777 4 1)",
R"(6666 1111 1111 )",
R"( 22 33 )",
R"(6666 1111 1111)",
R"( 22 33)",
}
},
{
{
R"(??????/\????????)",
R"(????_/./????????)",
R"(??????/\)",
R"(????_/./)",
R"(?,-' `-:.,-'/)",
R"(> o )<) _ ( )",
R"(> o )<) _ ()",
R"(?`-._ _.:'?`-.\)",
R"(?????``?\;??????)",
R"(?????``?\;)",
},
{
R"( 22 )",
R"( 1212 )",
R"( 22)",
R"( 1212)",
R"( 111 11116666)",
R"(1 4 777 1 6 )",
R"(1 4 777 1 6)",
R"( 1111 1111 6666)",
R"( 33 22 )",
R"( 33 22)",
}
},
{
{
R"(_?????????_.*"\??????)",
R"(\'-._..-*` `'*-.??)",
R"(_?????????_.*"\)",
R"(\'-._..-*` `'*-.)",
R"(?) , (( o >)",
R"(/.`"*--.__)_.`_.-*`??)"
R"(/.`"*--.__)_.`_.-*`)"
},
{
R"(6 11222 )",
R"(6661111111 11111 )",
R"(6 11222)",
R"(6661111111 11111)",
R"( 6 3 77 4 1)",
R"(6661111111311311111 )",
R"(6661111111311311111)",
}
},
{
{
R"(??????/"*._?????????_)",
R"(??.-*'` `*-.._.-'/)",
R"(< o )) , ( )",
R"(< o )) , ()",
R"(??`*-._`._(__.--*"`.\)",
},
{
R"( 22211 6)",
R"( 11111 1111111666)",
R"(1 4 77 3 6 )",
R"(1 4 77 3 6)",
R"( 1111131131111111666)",
},
}