Compare commits
10 Commits
64f8df2592
...
a635d19cfa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a635d19cfa | ||
|
|
0965b02fed | ||
|
|
65bb0e0f66 | ||
|
|
2d11304ab4 | ||
|
|
026aa887f9 | ||
|
|
1bc283b195 | ||
|
|
0a5d5fc87c | ||
|
|
21c03264e6 | ||
|
|
58970e8a68 | ||
|
|
f155f0e088 |
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Aryadev Chavali
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
2
Makefile
2
Makefile
@@ -1,7 +1,7 @@
|
||||
CC=gcc
|
||||
CFLAGS=-Wall -Wextra -pedantic -ggdb -fsanitize=address
|
||||
LIBS=-lm -lraylib
|
||||
OBJECTS=main.o
|
||||
OBJECTS=files.o main.o
|
||||
OUT=sandpile.out
|
||||
ARGS=
|
||||
|
||||
|
||||
50
files.c
Normal file
50
files.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/* file-handler.c
|
||||
* Created: 2023-08-25
|
||||
* Author: Aryadev Chavali
|
||||
* Description: Implementations of writing and loading state->from files
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stb/stb_image_write.h>
|
||||
|
||||
#include "./lib.h"
|
||||
|
||||
#define CHUNK_SIZE 1024
|
||||
|
||||
bool write_to_png(state_t *state, const char *filepath)
|
||||
{
|
||||
unsigned char *image_data =
|
||||
calloc(3 * state->dwidth * state->dwidth, sizeof(*image_data));
|
||||
|
||||
size_t image_ptr = 0;
|
||||
|
||||
for (size_t i = 0; i < state->dwidth; ++i)
|
||||
for (size_t j = 0; j < state->dwidth; ++j, image_ptr += 3)
|
||||
{
|
||||
unsigned char colour[3] = {0};
|
||||
uint64_t sandpile = state->data[(i * state->dwidth) + j];
|
||||
if (sandpile == 0)
|
||||
colour[0] = colour[1] = colour[2] = 0;
|
||||
else if (sandpile == 1)
|
||||
colour[0] = colour[2] = 255;
|
||||
else if (sandpile == 2)
|
||||
{
|
||||
colour[0] = 255;
|
||||
colour[1] = colour[2] = 20;
|
||||
}
|
||||
else if (sandpile == 3)
|
||||
{
|
||||
colour[2] = 255;
|
||||
colour[0] = colour[1] = 20;
|
||||
}
|
||||
|
||||
memcpy(image_data + image_ptr, colour, sizeof(*colour) * 3);
|
||||
}
|
||||
|
||||
stbi_write_png(filepath, state->dwidth, state->dwidth, 3, image_data,
|
||||
3 * state->dwidth);
|
||||
free(image_data);
|
||||
return true;
|
||||
}
|
||||
9
lib.h
9
lib.h
@@ -2,21 +2,22 @@
|
||||
#define LIB_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct State
|
||||
{
|
||||
// Sandpiles
|
||||
unsigned char *data;
|
||||
uint64_t *data;
|
||||
size_t dwidth;
|
||||
|
||||
size_t window_len;
|
||||
int multiplier;
|
||||
double multiplier;
|
||||
|
||||
bool thread_alive;
|
||||
uint64_t payload;
|
||||
} state_t;
|
||||
|
||||
bool load_from_file(state_t *state, const char *filepath);
|
||||
bool write_to_file(state_t *state, const char *filepath);
|
||||
bool write_to_png(state_t *state, const char *filepath);
|
||||
|
||||
#endif
|
||||
|
||||
104
main.c
104
main.c
@@ -15,14 +15,23 @@
|
||||
|
||||
#include "./lib.h"
|
||||
|
||||
void step(state_t *state)
|
||||
struct StepArg
|
||||
{
|
||||
state->data[((state->dwidth / 2) * state->dwidth) + (state->dwidth / 2)]++;
|
||||
for (size_t i = 0; i < state->dwidth; ++i)
|
||||
for (size_t j = 0; j < state->dwidth; ++j)
|
||||
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)
|
||||
{
|
||||
unsigned char *references[] = {
|
||||
pthread_mutex_lock(&mutex);
|
||||
uint64_t *references[] = {
|
||||
(j == 0) ? NULL : &state->data[((i)*state->dwidth) + j - 1],
|
||||
(i == state->dwidth - 1)
|
||||
? NULL
|
||||
@@ -33,36 +42,65 @@ void step(state_t *state)
|
||||
(i == 0) ? NULL : &state->data[((i - 1) * state->dwidth) + j]};
|
||||
for (size_t k = 0; k < 4; ++k)
|
||||
if (references[k])
|
||||
*references[k] += 1;
|
||||
state->data[(i * state->dwidth) + j] = 0;
|
||||
*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)
|
||||
{
|
||||
state_t *state = input;
|
||||
while (state->thread_alive)
|
||||
step(state);
|
||||
struct StepArg *arg = input;
|
||||
while (arg->state->thread_alive)
|
||||
step(*arg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
bool completed_avalanche(state_t *state)
|
||||
{
|
||||
state_t state = {NULL, 512, 512, 0, true};
|
||||
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;
|
||||
|
||||
pthread_t step_thread;
|
||||
pthread_create(&step_thread, NULL, &compute_thread, &state);
|
||||
// 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);
|
||||
|
||||
while (!WindowShouldClose())
|
||||
const int DELTA = 100;
|
||||
bool done = false;
|
||||
for (uint64_t ticks = 0, prev_ticks = 0; !WindowShouldClose(); ++ticks)
|
||||
{
|
||||
if (IsKeyPressed(KEY_UP) || IsKeyDown(KEY_UP))
|
||||
{
|
||||
@@ -85,6 +123,21 @@ int main(void)
|
||||
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);
|
||||
@@ -97,16 +150,16 @@ int main(void)
|
||||
for (size_t i = 0; i < state.dwidth; ++i)
|
||||
for (size_t j = 0; j < state.dwidth; ++j)
|
||||
{
|
||||
Color c = {0};
|
||||
unsigned char sandpile = state.data[(i * 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 = GREEN;
|
||||
c = MAGENTA;
|
||||
else if (sandpile == 2)
|
||||
c = PURPLE;
|
||||
c = RED;
|
||||
else if (sandpile == 3)
|
||||
c = YELLOW;
|
||||
c = BLUE;
|
||||
|
||||
DrawRectangle(i * state.multiplier, j * state.multiplier,
|
||||
state.multiplier, state.multiplier, c);
|
||||
@@ -116,7 +169,16 @@ int main(void)
|
||||
}
|
||||
|
||||
if (state.thread_alive)
|
||||
pthread_cancel(step_thread);
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user