diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | platforms.cpp | 101 | ||||
-rw-r--r-- | setup.cpp | 138 | ||||
-rw-r--r-- | sim.cpp | 168 | ||||
-rw-r--r-- | sim.hpp | 20 | ||||
-rw-r--r-- | util.cpp | 40 |
6 files changed, 338 insertions, 131 deletions
@@ -1,6 +1,6 @@ WARNINGS=-Wall -Wextra -Wshadow -Wconversion -Wpedantic -pedantic -std=gnu++11 -Wno-unused-function -Wimplicit-fallthrough LIBS=-ldl `pkg-config --libs --cflags sdl2 gl` -l:libbox2d.a -DEBUG_CFLAGS=$(CFLAGS) $(WARNINGS) $(LIBS) -DDEBUG -O0 -g +DEBUG_CFLAGS=$(CFLAGS) $(WARNINGS) $(LIBS) -DDEBUG -O0 -g3 obj/debug: physics obj/sim.so obj touch obj/debug physics: *.[ch]* diff --git a/platforms.cpp b/platforms.cpp index d208e50..e52b302 100644 --- a/platforms.cpp +++ b/platforms.cpp @@ -1,3 +1,15 @@ +#define PLATFORM_RADIUS_MIN 0.2f +#define PLATFORM_RADIUS_MAX 5.0f +#define PLATFORM_MOVE_SPEED_MIN 0.1f +#define PLATFORM_MOVE_SPEED_MAX 5.0f +#define PLATFORM_ROTATE_SPEED_MIN -3.0f +#define PLATFORM_ROTATE_SPEED_MAX +3.0f + +// additional cost for each additional meter of platform radius +#define PLATFORM_RADIUS_COST 1.0f +#define PLATFORM_MOVE_SPEED_COST 1.0f +#define PLATFORM_ROTATE_SPEED_COST 1.0f + // how far right could any part of this platform possibly go? static float platform_rightmost_x(Platform const *platform) { float angle; @@ -139,21 +151,8 @@ static void platforms_render(State *state, Platform *platforms, u32 nplatforms) } } -static uintptr_t platform_to_user_data(State *state, Platform *platform) { - return USER_DATA_PLATFORM | (uintptr_t)(platform - state->platforms); -} - -static Platform *platform_from_user_data(State *state, uintptr_t user_data) { - if ((user_data & USER_DATA_TYPE) == USER_DATA_PLATFORM) { - uintptr_t index = user_data & USER_DATA_INDEX; - return &state->platforms[index]; - } else { - return NULL; - } -} - // sets platform->body to a new Box2D body. -static void platform_make_body(State *state, Platform *platform) { +static void platform_make_body(State *state, Platform *platform, u32 index) { b2World *world = state->world; float radius = platform->radius; @@ -175,18 +174,12 @@ static void platform_make_body(State *state, Platform *platform) { b2FixtureDef fixture; fixture.shape = &shape; fixture.friction = 0.5f; - fixture.userData.pointer = platform_to_user_data(state, platform); + fixture.userData.pointer = USER_DATA_PLATFORM | index; body->CreateFixture(&fixture); - if (platform->moves) { - float speed = platform->move_speed; - v2 p1 = platform->move_p1, p2 = platform->move_p2; - v2 direction = v2_normalize(v2_sub(p2, p1)); - v2 velocity = v2_scale(direction, speed); - body->SetLinearVelocity(v2_to_b2(velocity)); - } - body->SetAngularVelocity(platform->rotate_speed); - + + // velocity and rotate speed will be set when setup_reset is called + platform->body = body; } @@ -196,8 +189,15 @@ public: this->state = state_; } bool ReportFixture(b2Fixture *fixture) { - platform = platform_from_user_data(state, fixture->GetUserData().pointer); - return !platform; // if we haven't found a platform, keep going + uintptr_t userdata = fixture->GetUserData().pointer; + if (userdata & USER_DATA_PLATFORM) { + u32 index = (u32)(userdata & ~USER_DATA_PLATFORM); + assert(index < state->nplatforms); + platform = &state->platforms[index]; + return false; // we can stop now + } else { + return true; // keep going + } } Platform *platform = NULL; State *state = NULL; @@ -227,7 +227,7 @@ static void platform_delete(State *state, Platform *platform) { // set this platform to last platform platforms[index] = platforms[nplatforms-1]; memset(&platforms[nplatforms-1], 0, sizeof(Platform)); - platforms[index].body->GetFixtureList()->GetUserData().pointer = platform_to_user_data(state, &platforms[index]); + platforms[index].body->GetFixtureList()->GetUserData().pointer = index | USER_DATA_PLATFORM; } else { // platform is at end of array; don't need to do anything special memset(&platforms[index], 0, sizeof(Platform)); @@ -237,11 +237,11 @@ static void platform_delete(State *state, Platform *platform) { } static float platform_cost(Platform const *platform) { - float cost = platform->radius; + float cost = platform->radius * PLATFORM_RADIUS_COST; if (platform->moves) - cost += platform->move_speed; + cost += platform->move_speed * PLATFORM_MOVE_SPEED_COST; if (platform->rotates) - cost += platform->rotate_speed; + cost += fabsf(platform->rotate_speed) * PLATFORM_ROTATE_SPEED_COST; return cost; } @@ -252,3 +252,44 @@ static float platforms_cost(Platform const *platforms, u32 nplatforms) { } return total_cost; } + +static void platform_write_to_file(Platform const *p, FILE *fp) { + fwrite_float(fp, p->radius); + fwrite_float(fp, p->start_angle); + fwrite_u32(fp, p->color); + + u8 flags = (u8)(p->moves * 1) | (u8)(p->rotates * 2); + fwrite_u8(fp, flags); + if (p->moves) { + fwrite_float(fp, p->move_speed); + fwrite_v2(fp, p->move_p1); + fwrite_v2(fp, p->move_p2); + } else { + fwrite_v2(fp, p->center); + } + if (p->rotates) { + fwrite_float(fp, p->rotate_speed); + } +} + +static void platform_read_from_file(Platform const *p, FILE *fp) { + p->radius = fread_float(fp); + p->start_angle = fread_float(fp); + p->color = fread_u32(fp); + + u8 flags = fread_u8(fp); + p->moves = (flags & 1) != 0; + p->rotates = (flags & 2) != 0; + + if (p->moves) { + p->move_speed = fread_float(fp); + p->move_p1 = fread_v2(fp); + p->move_p2 = fread_v2(fp); + } else { + p->center = fread_v2(fp); + } + + if (p->rotates) { + p->rotate_speed = fread_float(fp); + } +} diff --git a/setup.cpp b/setup.cpp new file mode 100644 index 0000000..19c6374 --- /dev/null +++ b/setup.cpp @@ -0,0 +1,138 @@ +// these only apply to computer-generated setups +#define SETUP_MIN_X 1.0f +#define SETUP_MAX_X 10.0f +#define SETUP_MIN_Y 1.0f +#define SETUP_MAX_Y 10.0f + +static v2 setup_rand_point(void) { + return V2( + rand_uniform(SETUP_MIN_X, SETUP_MAX_X), + rand_uniform(SETUP_MIN_Y, SETUP_MAX_Y) + ); +} + +#define PLATFORM_MOVE_CHANCE 0.5f // chance that the platform will be a moving one +#define PLATFORM_ROTATE_CHANCE 0.5f // chance that the platform will be a rotating one (platforms can be moving and rotating) +static void setup_random(State *state, Setup *setup, float cost_allowed) { + // how much "money" we have to spend + float cost_left = cost_allowed; + + while (cost_left > PLATFORM_RADIUS_MIN * PLATFORM_RADIUS_COST && setup->nplatforms < MAX_PLATFORMS) { + Platform *platform = &setup->platforms[setup->nplatforms++]; + + platform->color = rand_u32() | 0xFF; + float max_radius_allowed = cost_left / PLATFORM_RADIUS_COST; + if (max_radius_allowed > PLATFORM_RADIUS_MAX) + max_radius_allowed = PLATFORM_RADIUS_MAX; + platform->radius = rand_uniform(PLATFORM_RADIUS_MIN, max_radius_allowed); + platform->center = setup_rand_point(); + platform->start_angle = rand_uniform(0, PIf); + cost_left -= platform->radius * PLATFORM_RADIUS_COST; + + if (cost_left > PLATFORM_MOVE_SPEED_COST * PLATFORM_MOVE_SPEED_MIN) { + if (randf() < PLATFORM_MOVE_CHANCE) { + platform->moves = true; + float max_speed_allowed = cost_left / PLATFORM_MOVE_SPEED_COST; + if (max_speed_allowed > PLATFORM_MOVE_SPEED_MAX) + max_speed_allowed = PLATFORM_MOVE_SPEED_MAX; + platform->move_speed = rand_uniform(PLATFORM_MOVE_SPEED_MIN, max_speed_allowed); + platform->move_p1 = platform->center; + platform->move_p2 = setup_rand_point(); + cost_left -= platform->move_speed * PLATFORM_MOVE_SPEED_COST; + } + } + + if (cost_left > PLATFORM_ROTATE_SPEED_COST * 0.1f) { + if (randf() < PLATFORM_ROTATE_CHANCE) { + float max_rotate_speed_allowed = cost_left / PLATFORM_ROTATE_SPEED_COST; + if (max_rotate_speed_allowed > PLATFORM_ROTATE_SPEED_MAX) + max_rotate_speed_allowed = PLATFORM_ROTATE_SPEED_MAX; + platform->rotates = true; + platform->rotate_speed = rand_uniform(0.1f, max_rotate_speed_allowed); + if (rand() % 2) + platform->rotate_speed = -platform->rotate_speed; // clockwise + cost_left -= fabsf(platform->rotate_speed) * PLATFORM_ROTATE_SPEED_COST; + } + } + + platform_make_body(state, platform, setup->nplatforms - 1); + } + assert(cost_left >= 0); +} + +// make this setup the active one +static void setup_use(State *state, Setup *setup) { + memcpy(state->platforms, setup->platforms, setup->nplatforms * sizeof(Platform)); + state->nplatforms = setup->nplatforms; +} + +static void setup_reset(State *state) { + { // reset ball + Ball *ball = &state->ball; + b2World *world = state->world; + if (ball->body) + world->DestroyBody(ball->body); + + + ball->radius = 0.3f; + ball->pos = V2(BALL_STARTING_X, 10.0f); + + // create ball + b2BodyDef ball_def; + ball_def.type = b2_dynamicBody; + ball_def.position.Set(ball->pos.x, ball->pos.y); + b2Body *ball_body = ball->body = world->CreateBody(&ball_def); + + b2CircleShape ball_shape; + ball_shape.m_radius = ball->radius; + + b2FixtureDef ball_fixture; + ball_fixture.shape = &ball_shape; + ball_fixture.density = 1.0f; + ball_fixture.friction = 0.3f; + ball_fixture.restitution = 0.6f; // bounciness + + ball_body->CreateFixture(&ball_fixture); + + } + for (Platform *platform = state->platforms, *end = platform + state->nplatforms; platform != end; ++platform) { // reset platforms + b2Body *body = platform->body; + assert(body); + platform->angle = platform->start_angle; + if (platform->moves) platform->center = platform->move_p1; + body->SetTransform(v2_to_b2(platform->center), platform->angle); + if (platform->moves) { + float speed = platform->move_speed; + v2 p1 = platform->move_p1, p2 = platform->move_p2; + v2 direction = v2_normalize(v2_sub(p2, p1)); + v2 velocity = v2_scale(direction, speed); + body->SetLinearVelocity(v2_to_b2(velocity)); + } + body->SetAngularVelocity(platform->rotate_speed); + } + state->setting_move_p2 = false; + state->furthest_ball_x_pos = 0; + state->stuck_time = 0; + state->total_time = 0; + state->time_residue = 0; +} + +static float setup_score(State *state, Setup *setup) { + setup_use(state, setup); + setup_reset(state); + Ball *ball = &state->ball; + float starting_line = platforms_starting_line(setup->platforms, setup->nplatforms); + while (ball->body) { + simulate_time(state, 0.1f); + } + setup->score = ball->pos.x - starting_line; + return setup->score; +} + +static void setup_write_to_file(Setup *setup, FILE *fp) { + u32 nplatforms = setup->nplatforms; + fwrite_u32(fp, nplatforms); + for (u32 i = 0; i < nplatforms; ++i) { + platform_write_to_file(&setup->platforms[i], fp); + } +} @@ -1,3 +1,4 @@ +// @TODO: test read/write setups #include "gui.hpp" #ifdef _WIN32 #include <windows.h> @@ -39,41 +40,19 @@ static v2 b2_to_gl(State const *state, v2 box2d_coordinates) { #include "shaders.cpp" #include "platforms.cpp" -// render the ball -static void ball_render(State *state) { - GL *gl = &state->gl; - Ball *ball = &state->ball; - float ball_x = ball->pos.x, ball_y = ball->pos.y; - float ball_r = ball->radius; - ShaderBall *shader = &state->shader_ball; - - shader_start_using(gl, &shader->base); - - gl->UniformMatrix4fv(shader->uniform_transform, 1, GL_FALSE, state->transform.e); - gl->Uniform2f(shader->uniform_center, ball_x, ball_y); - gl->Uniform1f(shader->uniform_radius, ball_r); - - glBegin(GL_QUADS); - glColor3f(1,1,1); - glVertex2f(ball_x-ball_r, ball_y-ball_r); - glVertex2f(ball_x-ball_r, ball_y+ball_r); - glVertex2f(ball_x+ball_r, ball_y+ball_r); - glVertex2f(ball_x+ball_r, ball_y-ball_r); - glEnd(); - shader_stop_using(gl); -} - static void simulate_time(State *state, float dt) { + Ball *ball = &state->ball; + if (!ball->body) return; // we're done simulating float time_step = 0.01f; // fixed time step dt += state->time_residue; while (dt >= time_step) { - Ball *ball = &state->ball; b2World *world = state->world; world->Step(time_step, 8, 3); // step using recommended parameters - if (ball->body) { + { // update ball state->stuck_time += time_step; + state->total_time += time_step; b2Vec2 ball_pos = ball->body->GetPosition(); bool reached_bottom = ball_pos.y - ball->radius < state->bottom_y; // ball reached bottom line @@ -88,9 +67,12 @@ static void simulate_time(State *state, float dt) { } if (stuck) state->stuck_time = max_stuck_time; + return; // done simulating } else { ball->pos = b2_to_v2(ball_pos); - if (ball->pos.x > state->furthest_ball_x_pos) { + float rounded_pos = 0.01f * roundf(ball->pos.x * 100); + float rounded_record = 0.01f * roundf(state->furthest_ball_x_pos * 100); + if (rounded_pos > rounded_record) { // only update record if centimeter reading will change state->furthest_ball_x_pos = ball->pos.x; state->stuck_time = 0; } @@ -137,6 +119,32 @@ static void simulate_time(State *state, float dt) { state->time_residue = dt; } +// render the ball +static void ball_render(State *state) { + GL *gl = &state->gl; + Ball *ball = &state->ball; + float ball_x = ball->pos.x, ball_y = ball->pos.y; + float ball_r = ball->radius; + ShaderBall *shader = &state->shader_ball; + + shader_start_using(gl, &shader->base); + + gl->UniformMatrix4fv(shader->uniform_transform, 1, GL_FALSE, state->transform.e); + gl->Uniform2f(shader->uniform_center, ball_x, ball_y); + gl->Uniform1f(shader->uniform_radius, ball_r); + + glBegin(GL_QUADS); + glColor3f(1,1,1); + glVertex2f(ball_x-ball_r, ball_y-ball_r); + glVertex2f(ball_x-ball_r, ball_y+ball_r); + glVertex2f(ball_x+ball_r, ball_y+ball_r); + glVertex2f(ball_x+ball_r, ball_y-ball_r); + glEnd(); + shader_stop_using(gl); +} + +#include "setup.cpp" + static void correct_mouse_button(State *state, u8 *button) { if (*button == MOUSE_LEFT) { if (state->shift) { @@ -149,44 +157,6 @@ static void correct_mouse_button(State *state, u8 *button) { } } -static void setup_reset(State *state) { - { // reset ball - Ball *ball = &state->ball; - b2World *world = state->world; - if (ball->body) - world->DestroyBody(ball->body); - - - ball->radius = 0.3f; - ball->pos = V2(BALL_STARTING_X, 10.0f); - - // create ball - b2BodyDef ball_def; - ball_def.type = b2_dynamicBody; - ball_def.position.Set(ball->pos.x, ball->pos.y); - b2Body *ball_body = ball->body = world->CreateBody(&ball_def); - - b2CircleShape ball_shape; - ball_shape.m_radius = ball->radius; - - b2FixtureDef ball_fixture; - ball_fixture.shape = &ball_shape; - ball_fixture.density = 1.0f; - ball_fixture.friction = 0.3f; - ball_fixture.restitution = 0.6f; // bounciness - - ball_body->CreateFixture(&ball_fixture); - - } - for (Platform *p = state->platforms, *end = p + state->nplatforms; p != end; ++p) { // reset platforms - p->angle = p->start_angle; - if (p->moves) p->center = p->move_p1; - p->body->SetTransform(v2_to_b2(p->center), p->angle); - } - state->setting_move_p2 = false; - state->furthest_ball_x_pos = 0; - state->stuck_time = 0; -} #ifdef __cplusplus extern "C" @@ -310,17 +280,12 @@ void sim_frame(Frame *frame) { left_wall_body->CreateFixture(&left_wall_shape, 0); - { // initialize platforms - #if 0 - Platform *p = &state->platforms[0]; - p->start_angle = 0; - p->radius = 0.5f; - p->center = V2(1.5f, 1.5f); - p->color = 0xFF00FFFF; - platform_make_body(state, p); - state->nplatforms = (u32)(p - state->platforms + 1); - #endif + for (size_t i = 0; i < arr_count(state->generation); ++i) { + setup_random(state, &state->generation[i], 50); + printf("%f \n",setup_score(state, &state->generation[i])); + } + { Platform *b = &state->platform_building; b->radius = 1.5f; b->color = 0xFF00FF7F; @@ -345,6 +310,25 @@ void sim_frame(Frame *frame) { shaders_reload_if_necessary(state); #endif + + Font *font = &state->font; + Font *small_font = &state->small_font; + Platform *mouse_platform = platform_at_mouse_pos(state); + + if (state->simulating) { + // simulate physics + float dt = state->dt; + if (dt > 100) dt = 100; // prevent floating-point problems for very large dt's + simulate_time(state, dt); + if (keys_pressed[KEY_SPACE]) { + state->building = true; + state->simulating = false; + keys_pressed[KEY_SPACE] = 0; + setup_reset(state); + } + } + + { float half_height = 10.0f; float half_width = half_height * state->win_width / state->win_height; @@ -369,23 +353,6 @@ void sim_frame(Frame *frame) { state->mouse_pos = v3_xy(m4_mul_v3(state->inv_transform, mouse_gl_coords)); } - Font *font = &state->font; - Font *small_font = &state->small_font; - Platform *mouse_platform = platform_at_mouse_pos(state); - - if (state->simulating) { - // simulate physics - float dt = state->dt; - if (dt > 100) dt = 100; // prevent floating-point problems for very large dt's - simulate_time(state, dt); - if (keys_pressed[KEY_SPACE]) { - state->building = true; - state->simulating = false; - keys_pressed[KEY_SPACE] = 0; - setup_reset(state); - } - } - if (state->building) { Platform *platform_building = &state->platform_building; @@ -480,9 +447,12 @@ void sim_frame(Frame *frame) { platform_building->move_speed += speed_change_amount; } - platform_building->rotate_speed = clampf(platform_building->rotate_speed, -3, +3); - platform_building->move_speed = clampf(platform_building->move_speed, 0.1f, 5.0f); - platform_building->radius = clampf(platform_building->radius, 0.2f, 5.0f); + platform_building->rotate_speed = clampf(platform_building->rotate_speed, + PLATFORM_ROTATE_SPEED_MIN, PLATFORM_ROTATE_SPEED_MAX); + platform_building->move_speed = clampf(platform_building->move_speed, + PLATFORM_MOVE_SPEED_MIN, PLATFORM_MOVE_SPEED_MAX); + platform_building->radius = clampf(platform_building->radius, + PLATFORM_RADIUS_MIN, PLATFORM_RADIUS_MAX); platform_building->angle = fmodf(platform_building->angle, TAUf); for (u32 i = 0; i < input->nmouse_presses; ++i) { @@ -502,7 +472,7 @@ void sim_frame(Frame *frame) { Platform *p = &state->platforms[state->nplatforms++]; *p = *platform_building; p->color |= 0xFF; // set alpha to 255 - platform_make_body(state, p); + platform_make_body(state, p, state->nplatforms - 1); state->setting_move_p2 = false; platform_building->moves = false; } @@ -631,6 +601,12 @@ void sim_frame(Frame *frame) { pos.x = 1 - size.x; pos.y += size.y * 1.5f; text_render(state, small_font, text, pos); + // total time + snprintf(text, sizeof text - 1, "Total time: %.1fs", state->total_time); + size = text_get_size(state, small_font, text); + pos.x = 1 - size.x; + pos.y += size.y * 1.5f; + text_render(state, small_font, text, pos); } #if DEBUG @@ -128,12 +128,14 @@ typedef struct { typedef struct { b2Body *body; + + v2 center; + float radius; // half of the width of the platform + // save the starting position and rotation of the platform so we // can restore it to reset the setup float start_angle; - v2 center; - float radius; // half of the width of the platform float angle; bool moves; // does this platform move? @@ -162,6 +164,13 @@ typedef struct { // indicator that this Box2D fixture is a platform #define USER_DATA_PLATFORM (USER_DATA_TYPE_PLATFORM << USER_DATA_TYPE_SHIFT) +#define MAX_PLATFORMS 32 +typedef struct { + float score; // distance this setup can throw the ball + u32 nplatforms; + Platform platforms[MAX_PLATFORMS]; +} Setup; + typedef struct { bool initialized; @@ -193,6 +202,7 @@ typedef struct { Ball ball; float furthest_ball_x_pos; // furthest distance the ball has reached float stuck_time; // amount of time furthest_ball_x_pos hasn't changed for + float total_time; // amount of time the simulation has been running for float bottom_y; // y-position of "floor" (if y goes below here, it's over) float left_x; // y-position of left wall @@ -203,12 +213,14 @@ typedef struct { Font small_font; Platform platform_building; // the platform the user is currently placing - float platform_thickness; + u32 nplatforms; -#define MAX_PLATFORMS 1000 Platform platforms[MAX_PLATFORMS]; +#define GENERATION_SIZE 100 + Setup generation[GENERATION_SIZE]; + u32 tmp_mem_used; // this is not measured in bytes, but in MaxAligns #define TMP_MEM_BYTES (4L<<20) MaxAlign tmp_mem[TMP_MEM_BYTES / sizeof(MaxAlign)]; @@ -173,3 +173,43 @@ static int qsort_stricmp(const void *av, const void *bv) { #endif } +static void fwrite_bool(FILE *fp, bool x) { + fwrite(&x, sizeof x, 1, fp); +} + +static void fwrite_u32(FILE *fp, u32 x) { + fwrite(&x, sizeof x, 1, fp); +} + +static void fwrite_float(FILE *fp, float x) { + fwrite(&x, sizeof x, 1, fp); +} + +static void fwrite_v2(FILE *fp, v2 v) { + fwrite(&v, sizeof v, 1, fp); +} + +static void fread_bool(FILE *fp) { + bool x; + fread(&x, sizeof x, 1, fp); + return x; +} + +static void fread_u32(FILE *fp) { + u32 x; + fread(&x, sizeof x, 1, fp); + return x; +} + +static void fread_float(FILE *fp) { + float x; + fread(&x, sizeof x, 1, fp); + return x; +} + +static void fread_v2(FILE *fp) { + v2 v; + fread(&v, sizeof v, 1, fp); + return v; +} + |