/* main.c * Created: 2023-08-25 * Author: Aryadev Chavali * Description: Entry point of program */ #include #include #include #include #include #include #include #include "./lib.h" struct StepArg { state_t *state; size_t x_min, x_max, y_min, y_max; }; pthread_mutex_t mutex; void step(struct StepArg arg) { state_t *state = arg.state; for (size_t i = arg.x_min; i < arg.x_max; ++i) for (size_t j = arg.y_min; j < arg.y_max; ++j) if (state->data[(i * state->dwidth) + j] >= 4) { pthread_mutex_lock(&mutex); uint64_t *references[] = { (j == 0) ? NULL : &state->data[((i)*state->dwidth) + j - 1], (i == state->dwidth - 1) ? NULL : &state->data[((i + 1) * state->dwidth) + j], (j == state->dwidth - 1) ? NULL : &state->data[(i * state->dwidth) + j + 1], (i == 0) ? NULL : &state->data[((i - 1) * state->dwidth) + j]}; for (size_t k = 0; k < 4; ++k) if (references[k]) *references[k] += state->data[(i * state->dwidth) + j] / 4; state->data[(i * state->dwidth) + j] %= 4; pthread_mutex_unlock(&mutex); } } void *compute_thread(void *input) { struct StepArg *arg = input; while (arg->state->thread_alive) step(*arg); return NULL; } bool completed_avalanche(state_t *state) { for (size_t i = 0; i < state->dwidth; ++i) for (size_t j = 0; j < state->dwidth; ++j) if (state->data[(i * state->dwidth) + j] >= 4) return false; return true; } int main(int argc, char *argv[]) { // Setup "default" state state_t state = {NULL, 512, 512, 0, true, pow(2, 16)}; state.data = calloc(state.dwidth * state.dwidth, sizeof(*state.data)); state.multiplier = state.window_len / state.dwidth; state.data[(state.dwidth * state.dwidth / 2) + (state.dwidth / 2)] = state.payload; const float zoom = 0.125f; Camera2D camera = {0}; camera.zoom = 1.0f; // Initialise mutex for threads to use pthread_mutex_init(&mutex, NULL); // Setup quadrants (for threads) struct StepArg a = {&state, 0, state.dwidth / 2, 0, state.dwidth / 2}; struct StepArg b = {&state, 0, state.dwidth / 2, state.dwidth / 2, state.dwidth}; struct StepArg c = {&state, state.dwidth / 2, state.dwidth, 0, state.dwidth / 2}; struct StepArg d = {&state, state.dwidth / 2, state.dwidth, state.dwidth / 2, state.dwidth}; // Setup threads pthread_t thread_a, thread_b, thread_c, thread_d; pthread_create(&thread_a, NULL, &compute_thread, &a); pthread_create(&thread_b, NULL, &compute_thread, &b); pthread_create(&thread_c, NULL, &compute_thread, &c); pthread_create(&thread_d, NULL, &compute_thread, &d); InitWindow(state.window_len, state.window_len, "Abelian sand pile"); SetTargetFPS(60); const int DELTA = 100; bool done = false; for (uint64_t ticks = 0, prev_ticks = 0; !WindowShouldClose(); ++ticks) { if (IsKeyPressed(KEY_UP) || IsKeyDown(KEY_UP)) { Vector2 centre = {state.window_len / 2, state.window_len / 2}; Vector2 world_pos = GetScreenToWorld2D(centre, camera); camera.offset = centre; camera.target = world_pos; camera.zoom += zoom; if (camera.zoom < zoom) camera.zoom = zoom; } if (IsKeyPressed(KEY_DOWN) || IsKeyDown(KEY_DOWN)) { Vector2 centre = {state.window_len / 2, state.window_len / 2}; Vector2 world_pos = GetScreenToWorld2D(centre, camera); camera.offset = centre; camera.target = world_pos; camera.zoom -= zoom; if (camera.zoom < zoom) camera.zoom = zoom; } if (ticks - prev_ticks > DELTA && !done) { printf("Checking if avalanche is complete!\n"); if (completed_avalanche(&state)) { state.thread_alive = false; pthread_join(thread_a, NULL); pthread_join(thread_b, NULL); pthread_join(thread_c, NULL); pthread_join(thread_d, NULL); done = true; } prev_ticks = ticks; } if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { Vector2 delta = Vector2Scale(GetMouseDelta(), -1.0f / camera.zoom); camera.target = Vector2Add(camera.target, delta); } BeginDrawing(); ClearBackground(BLACK); BeginMode2D(camera); for (size_t i = 0; i < state.dwidth; ++i) for (size_t j = 0; j < state.dwidth; ++j) { Color c = {0}; uint64_t sandpile = state.data[(i * state.dwidth) + j]; if (sandpile == 0) c = BLACK; else if (sandpile == 1) c = MAGENTA; else if (sandpile == 2) c = RED; else if (sandpile == 3) c = BLUE; DrawRectangle(i * state.multiplier, j * state.multiplier, state.multiplier, state.multiplier, c); } EndMode2D(); EndDrawing(); } if (state.thread_alive) { state.thread_alive = false; pthread_join(thread_a, NULL); pthread_join(thread_b, NULL); pthread_join(thread_c, NULL); pthread_join(thread_d, NULL); } CloseWindow(); write_to_png(&state, "data.png"); free(state.data); return 0; }