formations/python-perfs/examples/sandpile.c

150 lines
3.9 KiB
C

#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#include <stdio.h>
#include <math.h>
#include <png.h>
void show_terrain(int width, int **terrain)
{
for (int x = 0; x < width; x++)
printf("-");
printf("\n");
for (int x = 0; x < width; x++) {
for (int y = 0; y < width; y++) {
if (terrain[x][y] == 0)
printf(" ");
else if (terrain[x][y] == 1)
printf("·");
else if (terrain[x][y] == 2)
printf("");
else if (terrain[x][y] == 3)
printf("");
else
printf("+");
}
printf("\n");
}
for (int x = 0; x < width; x++)
printf("-");
printf("\n");
}
int save_terrain(int width, int **terrain, char *filename)
{
png_image image;
png_byte row_pointers[width][width];
memset(&image, 0, (sizeof image));
image.version = PNG_IMAGE_VERSION;
image.format = PNG_FORMAT_FLAG_COLORMAP|PNG_FORMAT_FLAG_COLOR;
image.width = width;
image.height = width;
image.colormap_entries = 4;
for (int x = 0; x < width; x++)
for (int y = 0; y < width; y++)
row_pointers[x][y] = terrain[x][y];
if (png_image_write_to_file(&image, filename, 0,
row_pointers, 0,
"\xFF\xFF\xFF\xe9\xff\x70\x70\xd6\xff\xff\x70\xa6")){
return 0;
}
fprintf(stderr, "pngtopng: error: %s\n", image.message);
return 1;
}
#ifdef DEBUG
#define BOUND(a, low, high) (a < low ? low : (a > high ? high : a))
#define STORE_HEIGHT(x) by_height[BOUND(x, 0, 19)] += 1
#endif
void apply_gravity(int width, int **terrain)
{
bool did_something;
int div;
#ifdef DEBUG
int loops = 1;
uint64_t by_height[20] = {0};
#endif
while (1) {
did_something = false;
#ifdef DEBUG
loops += 1;
#endif
for (int x = 0; x < width; x++) {
for (int y = 0; y < width; y++) {
if (terrain[x][y] >= 4) {
#ifdef DEBUG
STORE_HEIGHT(terrain[x][y]);
#endif
did_something = true;
div = terrain[x][y] / 4;
terrain[x][y] = terrain[x][y] % 4;
terrain[x - 1][y] += div;
terrain[x + 1][y] += div;
terrain[x][y + 1] += div;
terrain[x][y - 1] += div;
}
}
}
if (!did_something) {
#ifdef DEBUG
fprintf(stderr, "looped %i times:\n", loops);
for (int i = 0; i < 9; i++) {
fprintf(stderr, "- %"PRIu64" times to break a pile of %i grains\n", by_height[i], i);
}
fprintf(stderr, "- %"PRIu64" times to break a pile of more than 13 grains\n", by_height[9]);
#endif
return;
}
}
}
int main(int argc, char **argv)
{
int width;
int height;
if (argc < 2) {
fprintf(stderr, "Please provide height.\n");
return EXIT_FAILURE;
}
if (sscanf(argv[1], "%i", &height) == EOF) {
fprintf(stderr, "Can't read height as an integer.\n");
return EXIT_FAILURE;
}
width = pow(height, 0.48);
printf("Spawning a terrain of %ix%i\n", width, width);
int **terrain = calloc(width, sizeof(int*));
int *data = calloc(width * width, sizeof(int));
for (int i = 0; i < width; i++) {
terrain[i] = data + i * width;
}
terrain[width / 2][width / 2] = height;
apply_gravity(width, terrain);
if (argc > 2) {
if (strcmp(argv[2], "/dev/null") != 0) {
printf("Saving terrain to file...\n");
if (save_terrain(width, terrain, argv[2]) == 0)
printf("Terrain saved\n");
else
printf("Can't save terrain\n");
}
}
else {
show_terrain(width, terrain);
}
return EXIT_SUCCESS;
}