Last active
April 5, 2025 16:01
-
-
Save GermanHoyos/116761462da1c042f158c9a123cbbb1c to your computer and use it in GitHub Desktop.
Adrians Snake Proj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Written by German Adrian Hoyos | |
// Linkedin: https://www.linkedin.com/in/adrian-i-81a32615b/ | |
// Inspired by the artwork of: https://x.com/5tr4n0/status/1885114394331275707 | |
// | |
// to launch via git vscode bash terminal: "./runme.bat" | |
// | |
// +------------+----------------------+------------------+---------------------------------+ | |
// | Data Type | Size (Typical) | Description | Value Range | | |
// +------------+----------------------+------------------+---------------------------------+ | |
// | bool | 1 byte | True or false | false (0) to true (1) | | |
// | char | 1 byte | Character type | -128 to 127 (signed) | | |
// | | | | 0 to 255 (unsigned) | | |
// | int | 4 bytes | Integer type | -2,147,483,648 to 2,147,483,647 | | |
// | short | 2 bytes | Short integer | -32,768 to 32,767 | | |
// | long | 4 or 8 bytes | Long integer | -2B to 2B (4B), varies | | |
// | long long | 8 bytes | Larger integer | -9 quintillion to 9 quintillion | | |
// | float | 4 bytes | Floating point | ~±3.4E−38 to ±3.4E+38 | | |
// | double | 8 bytes | Double precision | ~±1.7E−308 to ±1.7E+308 | | |
// | long double| 8 or 16 bytes | Extended float | Varies by system | | |
// | void | N/A | No type | N/A | | |
// | wchar_t | 2 or 4 bytes | Wide character | 0 to 65,535 or beyond | | |
// | char16_t | 2 bytes | UTF-16 character | 0 to 65,535 | | |
// | char32_t | 4 bytes | UTF-32 character | 0 to 4,294,967,295 | | |
// +------------+----------------------+------------------+---------------------------------+ | |
// | |
// char c1 = -5; // May or may not work as expected (depends on compiler) | |
// signed char c2 = -5; // Always allows negative values | |
// unsigned char c3 = 250; // Always treats values as 0 to 255 | |
// | |
// reasing.h from raylib: https://www.raylib.com/index.html | |
// * t = current time (in any unit measure, but same unit as duration) | |
// * b = starting value to interpolate | |
// * c = the total change in value of b that needs to occur | |
// * d = total time it should take to complete (duration) | |
/**************** | |
** headers ** | |
** custom + ** | |
** standard ** | |
****************/ | |
#include "include/MasterHeader.h" | |
/**************** | |
** class ** | |
** definitions** | |
** ** | |
****************/ | |
class AnimatableRectangle | |
{ | |
// Each instantiation has a unique id | |
static int idIterator; | |
// member variables | |
public: | |
int id; | |
float x, y, h, w; | |
unsigned char r = 255, g = 0, b = 0, a = 0; | |
float startValue; | |
float endValue; | |
float duration; | |
float gameTimeFloat; | |
double animationStartTime; | |
bool animating; | |
bool animatingComplete = true; // true becuase when I instantiate this it should not be animating | |
bool trigger = false; | |
// member initializer list | |
AnimatableRectangle(float x, float y, float h, float w, float startValue = 255.0f, float endValue = 0.0f, float duration = 2.5f) | |
: id(idIterator++), x(x), y(y), h(h), w(w), startValue(startValue), endValue(endValue), duration(duration), gameTimeFloat(0.0f), | |
animationStartTime(0.0), animating(false) | |
{ | |
// additional inits ... growth: may init pre defined paths here based on some algorithm | |
} | |
// member function | |
void draw() | |
{ | |
listen(); // manually reset all rects with mouse | |
//debugMe(); | |
// explicit if logic / easier to debug | |
if (trigger == true && animating == false) | |
{ | |
trigger = false; | |
animating = true; | |
} | |
if (trigger == true && animating == true) | |
{ | |
trigger = false; | |
animatingComplete = true; | |
} | |
if (animating == true && animatingComplete == true) | |
{ | |
animationStartTime = GetTime(); | |
resetValues(); | |
animatingComplete = false; | |
} | |
if (animating == true && animatingComplete == false) | |
{ | |
animateAlpha(); | |
} | |
DrawRectangle(x, y, h, w, Color {r, g, b, a}); | |
} | |
// Additional Easing Mathmatics / Functions: https://easings.net/ | |
// Cubic ease out function embedded within the class | |
// Easing function based on Robert Penner's easing equations | |
// Reference: /www.robertpenner.com/easing/ (Note: website not HTTPS secure) | |
// Function: Cubic Ease Out | |
// member function | |
float MyEaseCubicOut(float t, float b, float c, float d) | |
{ | |
t = t / d - 1.0f; | |
return (c * (t * t * t + 1.0f) + b); | |
} | |
// member function | |
void debugMe() | |
{ | |
string testStr = "Test"; | |
double gameTime = GetTime(); | |
string debugText = "Start Value: " + to_string(startValue) + "\n" | |
"End Value: " + to_string(endValue) + "\n" | |
"Duration: " + to_string(duration) + "\n" | |
"Game Time: " + to_string(gameTime) + "\n" | |
"Alpha: " + to_string(a) + "\n" | |
"Animating: " + to_string(animating) + "\n" | |
"Animation Start time: " + to_string(animationStartTime) + "\n" | |
"Game Time Float: " + to_string(gameTimeFloat) + "\n" | |
"My Id: " + to_string(id) ; | |
DrawText(debugText.c_str(), x + 20, y - 20, 3, GREEN); | |
} | |
// member function | |
void listen() | |
{ | |
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) | |
{ | |
animating = true; | |
resetValues(); | |
animationStartTime = GetTime(); | |
} | |
} | |
// member function | |
void animateAlpha() | |
{ | |
gameTimeFloat = GetTime() - animationStartTime; | |
a = MyEaseCubicOut(gameTimeFloat, startValue, endValue - startValue, duration); | |
if (a > 250) | |
{ | |
r = 255; | |
g = 0; | |
b = 0; | |
} | |
if (a > 240 && a < 250) | |
{ | |
r = 0; | |
g = 200; | |
b = 0; | |
} | |
if (a < 240) | |
{ | |
// experimenting with various start positions and tween choices | |
// g = 0; | |
// r = MyEaseCubicOut(gameTimeFloat, startValue, endValue - startValue, duration); | |
r = 0; | |
g = MyEaseCubicOut(gameTimeFloat, 110, endValue - 110, duration); | |
b = MyEaseCubicOut(gameTimeFloat, startValue, endValue - startValue, duration); | |
} | |
if (gameTimeFloat >= duration) // a <= endvalue || gameTimeFloat >= duration ... test both cases | |
{ | |
animating = false; | |
animatingComplete = true; | |
resetValues(); | |
} | |
} | |
// member function | |
void resetValues() | |
{ | |
a = 0.0f; | |
gameTimeFloat = 0.0f; | |
} | |
}; | |
// Static variables initialization | |
int AnimatableRectangle::idIterator = 1; | |
class Traverser { | |
// Each instantiation has a unique id | |
static int traverserId; | |
// member variables | |
public: | |
int id; | |
int cell; | |
int ticks = 0; | |
int pivots = 0; | |
int direction = 0; | |
int proposedDirection; | |
int validatedDirection; | |
bool fullSpeedTraverse = true; | |
bool up = false, down = false, left = false, right = false; | |
bool violation = false; | |
// member initializer list | |
Traverser(int cell) | |
: id(traverserId++), cell(cell) {} | |
// grid planes | |
/* 64 x 64 starting at 1 ... 4225 | |
[-64] if current cell is > than 64 | |
** | |
** | |
** | |
[-1]******************[+1] | |
** | |
** | |
** | |
[+64] if current cell is < 4161 | |
*/ | |
// member function | |
void running() | |
{ | |
// 1 = up | |
// 2 = down | |
// 3 = left | |
// 4 = right | |
// Randomizer 1 through 4 possibilities | |
// Controlling pivots dampens the amount of directional changes, however this is in | |
// relation to "ticks" as well... so (pivots > x) = X is not the exact amount of | |
// ticks before a directional change | |
pivots++; | |
if (pivots > 5) | |
{ | |
// 1 = up | |
// 2 = down | |
// 3 = left | |
// 4 = right | |
// To prevent double backs: | |
// (PROGENITOR) if none of the below is violated | |
// -- if up then down then force left or right (case ALPHA) | |
// -- if down then up then force left or right (case BETA) | |
// -- if right then left then force up or down (case CHARLIE) | |
// -- if left then right then force up or down (case DELTA) | |
// Randomizer 1 through 4 possibilities | |
// This randomizer is the (PROGENITOR) | |
random_device rd_1; | |
mt19937 gen_1(rd_1()); | |
uniform_int_distribution<int> dist_1(1, 4); | |
proposedDirection = dist_1(gen_1); | |
// Check if the proposed direction is different from the current direction | |
if (proposedDirection != direction) | |
{ | |
violation = false; // Reset violation before checking | |
// Check for double-backs and apply rules | |
switch (direction) | |
{ | |
case 1: // Current direction is up | |
if (proposedDirection == 2) // Going down, which is a double-back | |
{ | |
violation = true; | |
// If up -> down, force left or right (cases ALPHA and BETA) | |
random_device rd_2; | |
mt19937 gen_2(rd_2()); | |
uniform_int_distribution<int> dist_2(3, 4); | |
direction = dist_2(gen_2); | |
} | |
break; | |
case 2: // Current direction is down | |
if (proposedDirection == 1) // Going up, which is a double-back | |
{ | |
violation = true; | |
// If down -> up, force left or right (cases ALPHA and BETA) | |
random_device rd_2; | |
mt19937 gen_2(rd_2()); | |
uniform_int_distribution<int> dist_2(3, 4); | |
direction = dist_2(gen_2); | |
} | |
break; | |
case 3: // Current direction is left | |
if (proposedDirection == 4) // Going right, which is a double-back | |
{ | |
violation = true; | |
// If left -> right, force up or down (cases CHARLIE and DELTA) | |
random_device rd_3; | |
mt19937 gen_3(rd_3()); | |
uniform_int_distribution<int> dist_3(1, 2); | |
direction = dist_3(gen_3); | |
} | |
break; | |
case 4: // Current direction is right | |
if (proposedDirection == 3) // Going left, which is a double-back | |
{ | |
violation = true; | |
// If right -> left, force up or down (cases CHARLIE and DELTA) | |
random_device rd_3; | |
mt19937 gen_3(rd_3()); | |
uniform_int_distribution<int> dist_3(1, 2); | |
direction = dist_3(gen_3); | |
} | |
break; | |
} | |
} | |
// Only change direction if no violation has occurred | |
if (!violation) | |
{ | |
direction = proposedDirection; | |
} | |
// Reset violation flag and pivots counter | |
violation = false; | |
pivots = 0; | |
} | |
// Controlling ticks dampens the amount of cells changed per second | |
ticks++; | |
// Make fullSpeedTraverse = false to dampen with ticks | |
if (ticks > 1 || fullSpeedTraverse == false) | |
{ | |
// Up | |
if (direction == 1 && cell > 64) | |
{ | |
cell -= 65; | |
ticks = 0; | |
} | |
// top row handle (SPECIAL CASE) | |
if (direction == 1 && cell <= 64) | |
{ | |
cell += (4225 - cell); | |
ticks = 0; | |
} | |
// Down | |
if (direction == 2 && cell < 4161) | |
{ | |
cell += 65; | |
ticks = 0; | |
} | |
// bottom row handle (SPECIAL CASE) | |
if (direction == 2 && cell >= 4161) | |
{ | |
cell -= (cell - 64); // Wrap to the bottom | |
ticks = 0; | |
} | |
// Left | |
if (direction == 3) | |
{ | |
cell += 1; | |
ticks = 0; | |
} | |
// Right | |
if (direction == 4) | |
{ | |
cell -= 1; | |
ticks = 0; | |
} | |
} | |
} | |
// member function | |
void selfDestruct() | |
{ | |
// only will implement if I see a reason to self destruct. current iteration is infinite since no | |
// traversing is out of bounds | |
} | |
}; | |
int Traverser::traverserId = 1; | |
/**************** | |
** global ** | |
** scope ** | |
** ** | |
****************/ | |
vector<AnimatableRectangle> rectangleList; | |
vector<Traverser> traverserList; | |
/**************** | |
** C++ 17 ** | |
** main entry ** | |
** ** | |
****************/ | |
int main() | |
{ | |
// setup window / game context | |
int screenWidth = 800; | |
int screenHeight = 600; | |
raylib::Window w(screenWidth, screenHeight, "Adrians Snake Proj"); | |
SetTargetFPS(40); | |
// non looped inits | |
Traverser traveler = Traverser(600); | |
for (int i = 0; i < 60; i++) | |
{ | |
int rectStartCell; | |
random_device rd_1; mt19937 gen_1(rd_1()); uniform_int_distribution<int> dist_1(1, 4225); | |
rectStartCell = dist_1(gen_1); | |
Traverser newTraverser = Traverser(rectStartCell); | |
traverserList.push_back(newTraverser); | |
} | |
// algorithm inits | |
int tickCounter = 0; // delay for every other tick using modulas | |
int startPos_1 = 0; | |
// grid inits 65 x 65 = 4225 rects | |
int rows = 65; // 65 | |
int columns = 65; // 65 | |
int rectangleSize = 8; // 8 | |
int gapLength = 1; // 1 | |
// create a 10x10 grid | |
for (int row = 0; row < rows; row++) | |
{ | |
for (int column = 0; column < columns; column++) | |
{ | |
int x = column * (rectangleSize + gapLength); | |
int y = row * (rectangleSize + gapLength); | |
AnimatableRectangle newRect = AnimatableRectangle(x + 213,y + 7,rectangleSize,rectangleSize); | |
rectangleList.push_back(newRect); | |
} | |
} | |
/**************** | |
** start ** | |
** game loop ** | |
** ** | |
****************/ | |
// close window on escape key | |
while (!w.ShouldClose()) | |
{ | |
/**************** | |
** ** | |
** start draw ** | |
** context ** | |
****************/ | |
BeginDrawing(); | |
ClearBackground(BLACK); | |
TimeClass::displayGameTime(); // displays FPS and various other stats | |
// draw all rects | |
for (auto& rectangle : rectangleList) | |
{ | |
rectangle.draw(); | |
} | |
// draw all traversers | |
for (auto& traverserInst : traverserList) | |
{ | |
traverserInst.running(); // call running to move the traverser | |
rectangleList[traverserInst.cell].trigger = true; // trigger animation in the rectangle when the traverser is at that cell | |
} | |
EndDrawing(); | |
/**************** | |
** ** | |
** end draw ** | |
** context ** | |
****************/ | |
} | |
/**************** | |
** end ** | |
** game loop ** | |
** ** | |
****************/ | |
return 0; | |
} | |
/********************* | |
** ** | |
** notes + ** | |
** failed attempts ** | |
*********************/ | |
/* | |
// update object args + inits | |
ballPositionX = (int)EaseElasticOut(TimeClass::framesCounterResetable, 300, 800, 120 ); | |
DrawCircle(ballPositionX, 700, 10.0f, GREEN); | |
*/ | |
// if (tickCounter % 2 == 0) | |
// { | |
// if (startPos_1 < 4225) | |
// { | |
// rectangleList[startPos_1].animating = true; | |
// startPos_1 += 1; | |
// if (startPos_1 >= 4225) | |
// { | |
// startPos_1 = 0; | |
// } | |
// } | |
// } | |
// tickCounter++; | |
// proposedDirection = dist_1(gen_1); | |
// Snake logic proved to be tricky / was able to accomplish with c++ (case) | |
// if (proposedDirection == 1 && direction == 2) | |
// { | |
// violation = true; | |
// // This randomizer is for cases (ALPHA) and (BETA) | |
// random_device rd_2; mt19937 gen_2(rd_2()); uniform_int_distribution<int> dist_2(3, 4); | |
// direction = dist_2(gen_2); | |
// } | |
// if (proposedDirection == 2 && direction == 1) | |
// { | |
// violation = true; | |
// // This randomizer is for cases (ALPHA) and (BETA) | |
// random_device rd_2; mt19937 gen_2(rd_2()); uniform_int_distribution<int> dist_2(3, 4); | |
// direction = dist_2(gen_2); | |
// } | |
// if (proposedDirection == 3 && direction == 4) | |
// { | |
// violation = true; | |
// // This randomizer is for cases (CHARLIE) and (DELTA) | |
// random_device rd_3; mt19937 gen_3(rd_3()); uniform_int_distribution<int> dist_3(1, 2); | |
// direction = dist_3(gen_3); | |
// } | |
// if (proposedDirection == 4 && direction == 3) | |
// { | |
// violation = true; | |
// // This randomizer is for cases (CHARLIE) and (DELTA) | |
// random_device rd_3; mt19937 gen_3(rd_3()); uniform_int_distribution<int> dist_3(1, 2); | |
// direction = dist_3(gen_3); | |
// } | |
// // if no directional violation has occured | |
// if (!violation) | |
// { | |
// direction = dist_1(gen_1); | |
// } | |
//validatedDirection = direction; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment