150 lines
3.9 KiB
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;
|
|
}
|