From 2975520aa94ecd23d5327aecc6d79730589dddcd Mon Sep 17 00:00:00 2001 From: hegdeadithyak Date: Sat, 18 Apr 2026 17:34:27 +0530 Subject: [PATCH 1/2] add flappy_puffer --- config/flappy_puffer.ini | 19 + ocean/flappy_puffer/binding.c | 28 ++ ocean/flappy_puffer/flappy_puffer.c | 533 ++++++++++++++++++++++++++++ ocean/flappy_puffer/flappy_puffer.h | 90 +++++ 4 files changed, 670 insertions(+) create mode 100644 config/flappy_puffer.ini create mode 100644 ocean/flappy_puffer/binding.c create mode 100644 ocean/flappy_puffer/flappy_puffer.c create mode 100644 ocean/flappy_puffer/flappy_puffer.h diff --git a/config/flappy_puffer.ini b/config/flappy_puffer.ini new file mode 100644 index 0000000000..907a951a7c --- /dev/null +++ b/config/flappy_puffer.ini @@ -0,0 +1,19 @@ +[base] +env_name = flappy_puffer + +[vec] +total_agents = 256 +num_threads = 4 + +[train] +gpus = 1 +total_timesteps = 10_000_000 +learning_rate = 0.001 +minibatch_size = 1024 +horizon = 64 + +[sweep] +method = Protein +metric = score +max_runs = 1 +gpus = 0 diff --git a/ocean/flappy_puffer/binding.c b/ocean/flappy_puffer/binding.c new file mode 100644 index 0000000000..c6a4baceb4 --- /dev/null +++ b/ocean/flappy_puffer/binding.c @@ -0,0 +1,28 @@ +#include "flappy_puffer.h" +#include "flappy_puffer.c" +#define Log FPLog + +#define OBS_SIZE FP_OBS_SIZE +#define NUM_ATNS 1 +#define ACT_SIZES {2} +#define OBS_TENSOR_T FloatTensor + +#define Env FlappyPuffer +#include "vecenv.h" + +void my_init(Env* env, Dict* kwargs) { + env->num_agents = 1; + + void* seed_ptr = dict_get(kwargs, "seed"); + env->rng = (seed_ptr != NULL) + ? (unsigned int)(uintptr_t)seed_ptr + : (unsigned int)time(NULL); + allocate_flappy(env); +} + +void my_log(FPLog* log, Dict* out) { + dict_set(out, "perf", log->perf); + dict_set(out, "score", log->score); + dict_set(out, "episode_length", log->episode_length); + dict_set(out, "n_episodes", log->n); +} \ No newline at end of file diff --git a/ocean/flappy_puffer/flappy_puffer.c b/ocean/flappy_puffer/flappy_puffer.c new file mode 100644 index 0000000000..99d0ba8954 --- /dev/null +++ b/ocean/flappy_puffer/flappy_puffer.c @@ -0,0 +1,533 @@ +#include "flappy_puffer.h" +#ifndef HEADLESS +#include "raylib.h" +#include "raymath.h" +#endif + +#include + +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else + #define GLSL_VERSION 100 +#endif + +#define COL_SKY_TOP CLITERAL(Color){ 72, 160, 255, 255} +#define COL_SKY_BOT CLITERAL(Color){185, 225, 255, 255} +#define COL_CLOUD CLITERAL(Color){255, 255, 255, 210} +#define COL_CLOUD_DARK CLITERAL(Color){230, 240, 255, 210} +#define COL_GROUND CLITERAL(Color){ 97, 62, 28, 255} +#define COL_GRASS CLITERAL(Color){ 72, 175, 40, 255} +#define COL_GRASS2 CLITERAL(Color){ 55, 145, 30, 255} +#define COL_PIPE CLITERAL(Color){ 42, 200, 58, 255} +#define COL_PIPE_LIGHT CLITERAL(Color){ 90, 230, 90, 255} +#define COL_PIPE_DARK CLITERAL(Color){ 25, 135, 35, 255} +#define COL_PIPE_CAP CLITERAL(Color){ 34, 170, 48, 255} +#define COL_PIPE_CAP_LT CLITERAL(Color){ 75, 210, 75, 255} +#define COL_SCORE_GOLD CLITERAL(Color){255, 215, 0, 255} +#define COL_SCORE_SHADOW CLITERAL(Color){ 80, 40, 0, 180} +#define COL_DEAD_OVERLAY CLITERAL(Color){ 0, 0, 0, 160} +#define COL_HUD_BG CLITERAL(Color){ 0, 0, 0, 120} +#define COL_WHITE WHITE +#define COL_BLACK BLACK + +#define FP_FPS 60 +#define NUM_CLOUDS 6 +#define CLOUD_SPEED 0.45f /* pixels per frame */ + +typedef struct Cloud Cloud; +struct Cloud { + float x, y; + float w, h; + float speed; +}; + +struct FPClient { + Texture2D puffer_tex; + bool has_puffer; + Font font; + bool has_font; + Cloud clouds[NUM_CLOUDS]; + float ground_offset; + int frame; +}; + +static inline float randf01(unsigned int* rng) { + return (float)rand_r(rng) / (float)RAND_MAX; +} + +static void reset_pipe(FPPipe* p, float x, unsigned int* rng) { + p->x = x; + float margin = (float)FP_PIPE_GAP * 0.5f + 30.0f; + float min_cy = margin; + float max_cy = (float)FP_PLAYABLE_H - margin; + p->gap_cy = min_cy + randf01(rng) * (max_cy - min_cy); + p->passed = false; +} + +static void compute_obs(FlappyPuffer* env) { + float* obs = env->observations; + + obs[0] = env->bird_y / (float)FP_PLAYABLE_H; + obs[1] = env->bird_vel / 20.0f; + + FPPipe* p1 = NULL, *p2 = NULL; + for (int i = 0; i < FP_MAX_PIPES; i++) { + float right = env->pipes[i].x + FP_PIPE_W; + if (right > FP_BIRD_X - FP_BIRD_R) { + if (!p1 || env->pipes[i].x < p1->x) { p2 = p1; p1 = &env->pipes[i]; } + else if (!p2 || env->pipes[i].x < p2->x) { p2 = &env->pipes[i]; } + } + } + + if (p1) { + obs[2] = (p1->x - FP_BIRD_X) / (float)FP_SCREEN_W; + obs[3] = p1->gap_cy / (float)FP_PLAYABLE_H; + obs[4] = (p1->gap_cy - FP_PIPE_GAP * 0.5f) / (float)FP_PLAYABLE_H; + obs[5] = (p1->gap_cy + FP_PIPE_GAP * 0.5f) / (float)FP_PLAYABLE_H; + } else { + obs[2] = 1.0f; obs[3] = 0.5f; obs[4] = 0.35f; obs[5] = 0.65f; + } + if (p2) { + obs[6] = (p2->x - FP_BIRD_X) / (float)FP_SCREEN_W; + obs[7] = p2->gap_cy / (float)FP_PLAYABLE_H; + } else { + obs[6] = 2.0f; obs[7] = 0.5f; + } +} + +void allocate_flappy(FlappyPuffer* env) { + assert(env->num_agents == 1); + env->observations = calloc(env->num_agents * FP_OBS_SIZE, sizeof(float)); + env->rewards = calloc(env->num_agents, sizeof(float)); + env->terminals = calloc(env->num_agents, sizeof(float)); + env->actions = calloc(env->num_agents, sizeof(float)); +} + +void free_flappy(FlappyPuffer* env) { + free(env->observations); + free(env->rewards); + free(env->terminals); + free(env->actions); + if (env->client) { + if (env->client->has_puffer) UnloadTexture(env->client->puffer_tex); + if (env->client->has_font) UnloadFont(env->client->font); + if (IsWindowReady()) CloseWindow(); + free(env->client); + env->client = NULL; + } +} + +void c_reset_fp(FlappyPuffer* env) { + env->bird_y = FP_PLAYABLE_H * 0.45f; + env->bird_vel = 0.0f; + env->alive = true; + env->score = 0; + env->tick = 0; + + for (int i = 0; i < FP_MAX_PIPES; i++) { + reset_pipe(&env->pipes[i], + (float)FP_SCREEN_W + 120.0f + i * FP_PIPE_SPACING, + &env->rng); + } + + for (int a = 0; a < env->num_agents; a++) { + env->rewards[a] = 0.0f; + env->terminals[a] = 0.0f; + } + + compute_obs(env); +} + +void c_step_fp(FlappyPuffer* env) { + if (!env->alive) { + c_reset_fp(env); + } + + env->tick++; + + if ((int)env->actions[0] == ACT_FLAP) { + env->bird_vel = FP_FLAP_VEL; + } + + env->bird_vel += FP_GRAVITY; + if (env->bird_vel > FP_MAX_VEL) env->bird_vel = FP_MAX_VEL; + if (env->bird_vel < -FP_MAX_VEL) env->bird_vel = -FP_MAX_VEL; + env->bird_y += env->bird_vel; + + float reward = FP_REWARD_ALIVE; + + if (env->bird_y + FP_BIRD_R >= (float)FP_PLAYABLE_H || + env->bird_y - FP_BIRD_R <= 0.0f) { + env->alive = false; + } + + for (int i = 0; i < FP_MAX_PIPES; i++) { + env->pipes[i].x -= FP_PIPE_SPEED; + + if (env->pipes[i].x + FP_PIPE_W < -20.0f) { + float max_x = 0.0f; + for (int j = 0; j < FP_MAX_PIPES; j++) { + if (env->pipes[j].x > max_x) max_x = env->pipes[j].x; + } + reset_pipe(&env->pipes[i], max_x + FP_PIPE_SPACING, &env->rng); + } + + if (!env->pipes[i].passed && + env->pipes[i].x + FP_PIPE_W * 0.5f < FP_BIRD_X) { + env->pipes[i].passed = true; + env->score++; + reward += FP_REWARD_PIPE; + } + + if (env->alive) { + float gap_top = env->pipes[i].gap_cy - FP_PIPE_GAP * 0.5f; + float gap_bot = env->pipes[i].gap_cy + FP_PIPE_GAP * 0.5f; + bool x_hit = (FP_BIRD_X + FP_BIRD_R > env->pipes[i].x) && + (FP_BIRD_X - FP_BIRD_R < env->pipes[i].x + FP_PIPE_W); + bool y_hit = (env->bird_y - FP_BIRD_R < gap_top) || + (env->bird_y + FP_BIRD_R > gap_bot); + if (x_hit && y_hit) env->alive = false; + } + } + + + if (!env->alive) { + reward = FP_REWARD_DEATH; + env->terminals[0] = 1.0f; + env->log.score += (float)env->score; + env->log.episode_length += (float)env->tick; + env->log.n++; + env->log.perf = env->log.score / env->log.n; + } else { + env->terminals[0] = 0.0f; + } + + env->rewards[0] = reward; + compute_obs(env); +} + + +static void draw_background(FPClient* cl) { + DrawRectangleGradientV(0, 0, + FP_SCREEN_W, FP_PLAYABLE_H, + COL_SKY_TOP, COL_SKY_BOT); + + for (int i = 0; i < NUM_CLOUDS; i++) { + Cloud* c = &cl->clouds[i]; + DrawEllipse((int)c->x, (int)c->y, (int)(c->w*0.55f), (int)(c->h*0.65f), COL_CLOUD_DARK); + DrawEllipse((int)(c->x + c->w*0.30f), (int)(c->y - c->h*0.18f), (int)(c->w*0.50f), (int)(c->h*0.75f), COL_CLOUD); + DrawEllipse((int)(c->x + c->w*0.65f), (int)(c->y + c->h*0.05f), (int)(c->w*0.42f), (int)(c->h*0.58f), COL_CLOUD_DARK); + DrawEllipse((int)(c->x + c->w*0.25f), (int)c->y, (int)(c->w*0.80f), (int)(c->h*0.50f), COL_CLOUD); + } +} + +static void draw_ground(FPClient* cl) { + int gy = FP_PLAYABLE_H; + + DrawRectangle(0, gy, FP_SCREEN_W, 16, COL_GRASS); + for (int x = ((int)cl->ground_offset % 32); x < FP_SCREEN_W; x += 32) { + DrawRectangle(x, gy, 16, 4, COL_GRASS2); + } + + DrawRectangle(0, gy + 16, FP_SCREEN_W, FP_GROUND_H - 16, COL_GROUND); + + for (int x = ((int)(cl->ground_offset * 0.5f) % 48); x < FP_SCREEN_W; x += 48) { + DrawRectangle(x, gy + 20, 20, FP_GROUND_H - 24, + CLITERAL(Color){ 115, 75, 35, 80}); + } +} + +static void draw_pipe(FPPipe* p) { + int px = (int)p->x; + int pw = FP_PIPE_W; + int capw = FP_PIPE_CAP_W; + int caph = FP_PIPE_CAP_H; + int capx = px - (capw - pw) / 2; + + float gap_top = p->gap_cy - FP_PIPE_GAP * 0.5f; + float gap_bot = p->gap_cy + FP_PIPE_GAP * 0.5f; + + int top_h = (int)gap_top; + if (top_h > 0) { + DrawRectangle(px, 0, pw, top_h, COL_PIPE); + DrawRectangle(px + 4, 0, 10, top_h, COL_PIPE_LIGHT); + DrawRectangle(px + pw - 5, 0, 5, top_h, COL_PIPE_DARK); + } + DrawRectangle(capx, top_h - caph, capw, caph, COL_PIPE_CAP); + DrawRectangle(capx + 5, top_h - caph, 10, caph, COL_PIPE_CAP_LT); + DrawRectangleLines(capx, top_h - caph, capw, caph, COL_PIPE_DARK); + + int bot_y = (int)gap_bot; + int bot_h = FP_PLAYABLE_H - bot_y; + if (bot_h > 0) { + DrawRectangle(px, bot_y, pw, bot_h, COL_PIPE); + DrawRectangle(px + 4, bot_y, 10, bot_h, COL_PIPE_LIGHT); + DrawRectangle(px + pw - 5, bot_y, 5, bot_h, COL_PIPE_DARK); + } + DrawRectangle(capx, bot_y, capw, caph, COL_PIPE_CAP); + DrawRectangle(capx + 5, bot_y, 10, caph, COL_PIPE_CAP_LT); + DrawRectangleLines(capx, bot_y, capw, caph, COL_PIPE_DARK); + + if (top_h > 0) DrawRectangleLines(px, 0, pw, top_h, COL_PIPE_DARK); + if (bot_h > 0) DrawRectangleLines(px, bot_y, pw, bot_h, COL_PIPE_DARK); +} + +static void draw_puffer(FPClient* cl, float y, float vel) { + float cx = FP_BIRD_X; + float cy = y; + float sz = FP_BIRD_DRAW_SZ; + + float angle = vel * 4.0f; + if (angle > 90.0f) angle = 90.0f; + if (angle < -35.0f) angle = -35.0f; + + if (cl->has_puffer) { + Rectangle src = {0, 0, + (float)cl->puffer_tex.width, + (float)cl->puffer_tex.height}; + Rectangle dst = {cx, cy, sz, sz}; + Vector2 origin = {sz * 0.5f, sz * 0.5f}; + DrawTexturePro(cl->puffer_tex, src, dst, origin, angle, WHITE); + } else { + float rad = sz * 0.45f; + float cxf = cx; + float cyf = cy; + + DrawEllipse((int)(cxf + 2), (int)(cyf + 3), + (int)(rad * 1.05f), (int)(rad * 0.92f), + CLITERAL(Color){0, 0, 0, 60}); + + DrawCircle((int)cxf, (int)cyf, (int)rad, + CLITERAL(Color){255, 190, 40, 255}); + + DrawEllipse((int)(cxf - 3), (int)(cyf - 2), + (int)(rad * 0.55f), (int)(rad * 0.45f), + CLITERAL(Color){255, 230, 130, 200}); + + float ang_rad = angle * DEG2RAD; + for (int s = 0; s < 8; s++) { + float a = s * (2.0f * PI / 8.0f) + ang_rad * 0.5f; + float cos_a = cosf(a), sin_a = sinf(a); + int x1 = (int)(cxf + rad * 0.72f * cos_a); + int y1 = (int)(cyf + rad * 0.72f * sin_a); + int x2 = (int)(cxf + (rad + 11.0f) * cos_a); + int y2 = (int)(cyf + (rad + 11.0f) * sin_a); + DrawLineEx((Vector2){(float)x1,(float)y1}, + (Vector2){(float)x2,(float)y2}, + 3.0f, + CLITERAL(Color){200, 130, 20, 255}); + DrawCircle(x2, y2, 3, + CLITERAL(Color){240, 160, 30, 255}); + } + + DrawCircle((int)(cxf + 9), (int)(cyf - 6), 7, + CLITERAL(Color){255, 255, 255, 255}); + DrawCircle((int)(cxf + 11), (int)(cyf - 5), 4, + CLITERAL(Color){15, 15, 50, 255}); + DrawCircle((int)(cxf + 12), (int)(cyf - 7), 2, + CLITERAL(Color){255, 255, 255, 255}); + + DrawLineEx( + (Vector2){cxf + 12.0f, cyf + 4.0f}, + (Vector2){cxf + 16.0f, cyf + 6.0f}, + 2.0f, CLITERAL(Color){180, 80, 20, 255}); + } +} + +static void draw_score(FPClient* cl, int score) { + const char* txt = TextFormat("%d", score); + int fs = 52; + int tw = MeasureText(txt, fs); + int sx = FP_SCREEN_W / 2 - tw / 2; + int sy = 32; + + DrawText(txt, sx + 3, sy + 3, fs, COL_SCORE_SHADOW); + DrawText(txt, sx, sy, fs, COL_SCORE_GOLD); +} + +/* ── game-over overlay ── */ +static void draw_game_over(int score, float best) { + DrawRectangle(0, 0, FP_SCREEN_W, FP_SCREEN_H, COL_DEAD_OVERLAY); + + const char* go = "GAME OVER"; + int fs_go = 58; + int tw_go = MeasureText(go, fs_go); + DrawText(go, FP_SCREEN_W/2 - tw_go/2 + 3, + FP_SCREEN_H/2 - 60 + 3, fs_go, + CLITERAL(Color){120, 0, 0, 255}); + DrawText(go, FP_SCREEN_W/2 - tw_go/2, + FP_SCREEN_H/2 - 60, fs_go, + CLITERAL(Color){255, 60, 60, 255}); + + const char* sc_txt = TextFormat("Score: %d", score); + int tw_sc = MeasureText(sc_txt, 34); + DrawText(sc_txt, FP_SCREEN_W/2 - tw_sc/2, FP_SCREEN_H/2 + 10, 34, + COL_SCORE_GOLD); + + if (best > 0) { + const char* best_txt = TextFormat("Best: %.0f", best); + int tw_b = MeasureText(best_txt, 26); + DrawText(best_txt, FP_SCREEN_W/2 - tw_b/2, FP_SCREEN_H/2 + 55, 26, + CLITERAL(Color){200, 200, 255, 255}); + } + + const char* hint = "SPACE / W / UP to flap"; + int tw_h = MeasureText(hint, 20); + DrawText(hint, FP_SCREEN_W/2 - tw_h/2, FP_SCREEN_H/2 + 105, 20, + CLITERAL(Color){200, 200, 200, 200}); +} + +static void draw_stats(FPLog* log) { + if (log->n < 1.0f) return; + const char* txt = TextFormat("Avg: %.1f Eps: %.0f", log->perf, log->n); + int tw = MeasureText(txt, 18); + DrawRectangle(FP_SCREEN_W - tw - 16, 6, tw + 12, 26, COL_HUD_BG); + DrawText(txt, FP_SCREEN_W - tw - 10, 10, 18, + CLITERAL(Color){200, 240, 255, 220}); +} + +static FPClient* make_fp_client(void) { +#ifndef HEADLESS + InitWindow(FP_SCREEN_W, FP_SCREEN_H, "Flappy Puffer"); + SetTargetFPS(FP_FPS); + + FPClient* cl = calloc(1, sizeof(FPClient)); + cl->frame = 0; + cl->ground_offset = 0.0f; + + const char* tex_paths[] = { + "/home/amma/PufferLib/resources/cpr/inflated_puff.png", + "resources/cpr/inflated_puff.png", + "../resources/cpr/inflated_puff.png", + "inflated_puff.png", + }; + for (int i = 0; i < 4; i++) { + if (FileExists(tex_paths[i])) { + cl->puffer_tex = LoadTexture(tex_paths[i]); + cl->has_puffer = true; + break; + } + } + + unsigned int rng = (unsigned int)time(NULL); + for (int i = 0; i < NUM_CLOUDS; i++) { + cl->clouds[i].x = (float)(rand_r(&rng) % FP_SCREEN_W); + cl->clouds[i].y = 40.0f + (float)(rand_r(&rng) % (FP_PLAYABLE_H / 2)); + cl->clouds[i].w = 80.0f + (float)(rand_r(&rng) % 80); + cl->clouds[i].h = 28.0f + (float)(rand_r(&rng) % 20); + cl->clouds[i].speed = 0.25f + (float)(rand_r(&rng) % 100) / 200.0f; + } + + return cl; +#else + return NULL; +#endif +} + +static void update_client(FPClient* cl, float pipe_speed) { + cl->frame++; + cl->ground_offset += pipe_speed; + + for (int i = 0; i < NUM_CLOUDS; i++) { + cl->clouds[i].x -= cl->clouds[i].speed; + if (cl->clouds[i].x + cl->clouds[i].w < 0) { + unsigned int rng = (unsigned int)(cl->frame * 7 + i * 31); + cl->clouds[i].x = (float)FP_SCREEN_W + 20.0f; + cl->clouds[i].y = 40.0f + (float)(rand_r(&rng) % (FP_PLAYABLE_H / 2)); + cl->clouds[i].w = 80.0f + (float)(rand_r(&rng) % 80); + cl->clouds[i].h = 28.0f + (float)(rand_r(&rng) % 20); + } + } +} + + +int c_render_fp(FlappyPuffer* env) { +#ifndef HEADLESS + if (!env->client) { + env->client = make_fp_client(); + } + FPClient* cl = env->client; + + update_client(cl, env->alive ? FP_PIPE_SPEED : 0.0f); + + BeginDrawing(); + ClearBackground(COL_SKY_TOP); + + draw_background(cl); + + for (int i = 0; i < FP_MAX_PIPES; i++) { + draw_pipe(&env->pipes[i]); + } + + draw_ground(cl); + draw_puffer(cl, env->bird_y, env->bird_vel); + + + draw_score(cl, env->score); + draw_stats(&env->log); + + if (!env->alive) { + draw_game_over(env->score, env->log.perf); + } + + EndDrawing(); + + if (WindowShouldClose() || IsKeyDown(KEY_ESCAPE)) { + CloseWindow(); + exit(0); + } + + if (IsKeyPressed(KEY_SPACE) || IsKeyPressed(KEY_W) || IsKeyPressed(KEY_UP)) { + return ACT_FLAP; + } +#endif + return ACT_NOOP; +} + +void c_close(FlappyPuffer* env) { + free_flappy(env); +} + +void c_reset(FlappyPuffer* env) { + c_reset_fp(env); +} + +void c_step(FlappyPuffer* env) { + c_step_fp(env); +} + +void c_render(FlappyPuffer* env) { + c_render_fp(env); +} + +void demo(void) { + FlappyPuffer env; + memset(&env, 0, sizeof(FlappyPuffer)); + env.num_agents = 1; + env.rng = (unsigned int)time(NULL); + + allocate_flappy(&env); + c_reset_fp(&env); + +#ifndef HEADLESS + // Initialise the window by calling render once + c_render_fp(&env); + + while (!WindowShouldClose()) { + int action = c_render_fp(&env); + env.actions[0] = (float)action; + c_step_fp(&env); + } +#else + printf("Demo built in HEADLESS mode. No visualization available.\n"); +#endif + + free_flappy(&env); +} + +int main() { + printf("Starting flappy_puffer_demo...\n"); + demo(); + return 0; +} \ No newline at end of file diff --git a/ocean/flappy_puffer/flappy_puffer.h b/ocean/flappy_puffer/flappy_puffer.h new file mode 100644 index 0000000000..37ad9ac6bc --- /dev/null +++ b/ocean/flappy_puffer/flappy_puffer.h @@ -0,0 +1,90 @@ +#pragma once + + +#include +#include +#include +#include +#include +#include + + +#define ACT_NOOP 0 +#define ACT_FLAP 1 + +#define FP_SCREEN_W 480 +#define FP_SCREEN_H 720 +#define FP_GROUND_H 80 +#define FP_PLAYABLE_H (FP_SCREEN_H - FP_GROUND_H) + + +#define FP_GRAVITY 0.44f +#define FP_FLAP_VEL -9.5f +#define FP_PIPE_SPEED 3.0f +#define FP_MAX_VEL 12.0f + +#define FP_PIPE_GAP 165 +#define FP_PIPE_W 72 +#define FP_PIPE_CAP_W 88 +#define FP_PIPE_CAP_H 24 +#define FP_PIPE_SPACING 280 +#define FP_MAX_PIPES 5 + + +#define FP_BIRD_X 115.0f +#define FP_BIRD_R 20.0f +#define FP_BIRD_DRAW_SZ 52.0f + + +#define FP_OBS_SIZE 8 + +#define FP_REWARD_ALIVE 0.01f +#define FP_REWARD_PIPE 1.00f +#define FP_REWARD_DEATH -1.00f + + + +typedef struct FPLog FPLog; +struct FPLog { + float perf; + float score; + float episode_length; + float n; +}; + +typedef struct FPPipe FPPipe; +struct FPPipe { + float x; + float gap_cy; + bool passed; +}; + + +typedef struct FPClient FPClient; + +typedef struct FlappyPuffer FlappyPuffer; +struct FlappyPuffer { + FPClient* client; + + int num_agents; + float bird_y; + float bird_vel; + bool alive; + int score; + int tick; + + FPPipe pipes[FP_MAX_PIPES]; + float* observations; + float* rewards; + float* terminals; + float* actions; + + FPLog log; + unsigned int rng; +}; + +void allocate_flappy (FlappyPuffer* env); +void free_flappy (FlappyPuffer* env); +void c_reset_fp (FlappyPuffer* env); +void c_step_fp (FlappyPuffer* env); +int c_render_fp (FlappyPuffer* env); \ No newline at end of file From 51122b339979bd479052c6ece207d95c4ff7e001 Mon Sep 17 00:00:00 2001 From: Kota Adithya Hegde Date: Tue, 28 Apr 2026 12:05:39 +0530 Subject: [PATCH 2/2] add resources --- ocean/flappy_puffer/flappy_puffer.c | 16 +++------------- resources/flappy_puffer/inflated_puff.png | Bin 0 -> 46433 bytes 2 files changed, 3 insertions(+), 13 deletions(-) create mode 100644 resources/flappy_puffer/inflated_puff.png diff --git a/ocean/flappy_puffer/flappy_puffer.c b/ocean/flappy_puffer/flappy_puffer.c index 99d0ba8954..69c52d2f73 100644 --- a/ocean/flappy_puffer/flappy_puffer.c +++ b/ocean/flappy_puffer/flappy_puffer.c @@ -396,19 +396,9 @@ static FPClient* make_fp_client(void) { cl->frame = 0; cl->ground_offset = 0.0f; - const char* tex_paths[] = { - "/home/amma/PufferLib/resources/cpr/inflated_puff.png", - "resources/cpr/inflated_puff.png", - "../resources/cpr/inflated_puff.png", - "inflated_puff.png", - }; - for (int i = 0; i < 4; i++) { - if (FileExists(tex_paths[i])) { - cl->puffer_tex = LoadTexture(tex_paths[i]); - cl->has_puffer = true; - break; - } - } + cl->puffer_tex = LoadTexture("../resources/flappy_puffer/inflated_puff.png"); + cl->has_puffer = true; + unsigned int rng = (unsigned int)time(NULL); for (int i = 0; i < NUM_CLOUDS; i++) { diff --git a/resources/flappy_puffer/inflated_puff.png b/resources/flappy_puffer/inflated_puff.png new file mode 100644 index 0000000000000000000000000000000000000000..c33e7b25c8266b038f0ea49d6b244ee95c6fd6e1 GIT binary patch literal 46433 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 literal 0 HcmV?d00001