185 lines
5.2 KiB
C
185 lines
5.2 KiB
C
/* main.c
|
|
* Created: 2023-08-25
|
|
* Author: Aryadev Chavali
|
|
* Description: Entry point of program
|
|
*/
|
|
#include <malloc.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
|
|
#include <raylib.h>
|
|
#include <raymath.h>
|
|
|
|
#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;
|
|
}
|