Skip to content

Instantly share code, notes, and snippets.

@GermanHoyos
Last active April 5, 2025 16:01
Show Gist options
  • Save GermanHoyos/116761462da1c042f158c9a123cbbb1c to your computer and use it in GitHub Desktop.
Save GermanHoyos/116761462da1c042f158c9a123cbbb1c to your computer and use it in GitHub Desktop.
Adrians Snake Proj
// 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