fast-abelian-sandpile/common.c

107 lines
2.6 KiB
C

#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <png.h>
typedef struct args {
int height;
char *png_filename;
bool print_to_stdout;
} args;
int parse_args(int argc, char **argv, args *args)
{
if (argc < 2) {
fprintf(stderr, "Usage:\n");
fprintf(stderr, " %s --stdout # To print as an ASCII table.\n", argv[0]);
fprintf(stderr, " %s file.png # To write a PNG file.\n", argv[0]);
return 1;
}
if (sscanf(argv[1], "%i", &(args->height)) == EOF) {
fprintf(stderr, "Can't read height as an integer.\n");
return 1;
}
if (argc > 2) {
if (strcmp(argv[2], "--stdout") == 0)
args->print_to_stdout = true;
else
args->png_filename = argv[2];
}
return 0;
}
static void print_line(int width)
{
printf("+");
for (int x = 0; x < width; x++)
printf("---+");
printf("\n");
}
void terrain_to_stdout(int width, int **terrain)
{
for (int x = 0; x < width; x++) {
print_line(width);
printf("|");
for (int y = 0; y < width; y++) {
if (terrain[x][y] == 0)
printf(" |");
else if (terrain[x][y] < 10)
printf(" %i |", terrain[x][y]);
else
printf("%3i|", terrain[x][y]);
}
printf("\n");
}
print_line(width);
}
#define ONE_GRAIN "\x3E\x92\xCC"
#define TWO_GRAINS "\x2A\x62\x8F"
#define THREE_GRAINS "\x13\x29\x3D"
#define CMAP "\xFF\xFF\xFF" ONE_GRAIN TWO_GRAINS THREE_GRAINS
int terrain_to_png(int width, int **terrain, char *filename)
{
png_image image;
png_byte *bytes;
bytes = malloc(sizeof(*bytes) * 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++)
bytes[x * width + y] = terrain[x][y];
if (png_image_write_to_file(&image, filename, 0, bytes, width, CMAP))
return 0;
fprintf(stderr, "pngtopng: error: %s\n", image.message);
return 1;
}
void save_terrain(int width, int **terrain, args *args)
{
if (args->png_filename)
terrain_to_png(width, terrain, args->png_filename);
if (args->print_to_stdout)
terrain_to_stdout(width, terrain);
}
int sandpile_width(int ngrains) {
int width;
width = pow(ngrains / 2 / 3.1415926535, 0.5) * 2 + 1;
width += 1 - width % 2; // Ensure width is odd (terrain has a center)
return width;
}