Compare commits

...

10 Commits

Author SHA1 Message Date
Aryadev Chavali
a635d19cfa +LICENSE 2023-08-28 19:58:21 +01:00
Aryadev Chavali
0965b02fed (main)+comments 2023-08-27 22:10:05 +01:00
Aryadev Chavali
65bb0e0f66 (main)+kill threads when avalanche has completed
After the so-called "avalanche", where the fractal has finished
forming as there are no more candidates for toppling, we no longer
need the threads to be working.  As an intensive operation, it makes
no sense to continue processing when we are sure there is no further
dynamic behaviour possible.

Using a tick based solution, every DELTA ticks we check if the
avalanche is completed.  If so, we clean up all the threads AND ensure
that we no longer need to be checking for avalanches.
2023-08-27 22:06:44 +01:00
Aryadev Chavali
2d11304ab4 ~file-handler->files,(main~>files)~migrated png save code to files.c 2023-08-27 21:53:28 +01:00
Aryadev Chavali
026aa887f9 +further multithreading
I split the grid into 4 quadrants which are managed by their own
thread.  Improves speed and uses more resources correctly.
2023-08-26 00:51:17 +01:00
Aryadev Chavali
1bc283b195 +write to image support
Writes output to "data.png"
2023-08-26 00:50:08 +01:00
Aryadev Chavali
0a5d5fc87c ~modified step,~state data is now unsigned 64 bit integer
Stopped adding a grain of sand per step.

Collapsing should work properly for any amount of sand grains,
regardless of whether it's 4 or 4000.  Previous code fixed it at 4
always, and how it distributed was so as well.  Now we distribute as
much sand as we can.

This comes from the idea that, instead of incrementing while
rendering, let's just set some n pixels in the centre and see how the
model distributes them.
2023-08-25 22:57:33 +01:00
Aryadev Chavali
21c03264e6 (main|file-handler)~fixed some stuff
Big bug with file handler: I can write data well, but reading it
causes some trace error which I cannot be bothered to fix.  So I'll
just work on making the step function way more efficient.
2023-08-25 20:03:23 +01:00
Aryadev Chavali
58970e8a68 (file-handler)+implemented write_to_file
First line is the data width, then the sand particles of each cell per
row.
2023-08-25 19:19:50 +01:00
Aryadev Chavali
f155f0e088 (file-handler)+impl file for file handling
In particular I've implemented a standardised complete FILE * reader
without relying on fseek (which may not work for pipes).
2023-08-25 19:08:32 +01:00
5 changed files with 160 additions and 26 deletions

21
LICENSE Normal file
View 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.

View File

@@ -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
View 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
View File

@@ -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
View File

@@ -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;
}