114 lines
2.7 KiB
C
114 lines
2.7 KiB
C
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
|
|
#include "common.c"
|
|
|
|
|
|
/// This one stores, for each cells, 4 pointers to the west, south,
|
|
/// north, and east cells so incrementing them is easy.
|
|
|
|
typedef struct cell cell;
|
|
|
|
struct cell {
|
|
cell *west;
|
|
cell *north;
|
|
cell *south;
|
|
cell *east;
|
|
int height;
|
|
};
|
|
|
|
|
|
static inline void fire(cell *cell) {
|
|
int div, rest;
|
|
|
|
div = cell->height / 4;
|
|
rest = cell->height % 4;
|
|
cell->height = rest;
|
|
cell->west->height += div;
|
|
cell->south->height += div;
|
|
cell->north->height += div;
|
|
cell->east->height += div;
|
|
}
|
|
|
|
|
|
void apply_gravity(int width, cell *cells)
|
|
{
|
|
bool did_something;
|
|
|
|
while (1) {
|
|
did_something = false;
|
|
for (int x = 0; x < width*width; x++) {
|
|
if (cells[x].height >= 4) {
|
|
did_something = true;
|
|
fire(cells+x);
|
|
}
|
|
}
|
|
if (!did_something)
|
|
return;
|
|
}
|
|
}
|
|
|
|
cell *sandpile_new(int width, int height)
|
|
{
|
|
cell **terrain = calloc(width, sizeof(cell*));
|
|
cell *data = calloc(width * width, sizeof(cell));
|
|
for (int i = 0; i < width; i++) {
|
|
terrain[i] = data + i * width;
|
|
}
|
|
terrain[width / 2][width / 2].height = height;
|
|
for (int x = 0; x < width; x++) {
|
|
for (int y = 0; y < width; y++) {
|
|
terrain[x][y].west = &terrain[x-1][y];
|
|
terrain[x][y].south = &terrain[x][y-1];
|
|
terrain[x][y].north = &terrain[x][y+1];
|
|
terrain[x][y].east = &terrain[x+1][y];
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
int **cells_to_array(int width, cell *cells) {
|
|
// Create terrain:
|
|
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;
|
|
}
|
|
|
|
// Organize cells in a 2d structure:
|
|
cell **cell_array = calloc(width, sizeof(cell*));
|
|
for (int i = 0; i < width; i++) {
|
|
cell_array[i] = cells + i * width;
|
|
}
|
|
|
|
// Copy cells to terrain:
|
|
for (int x = 0; x < width; x++)
|
|
for (int y = 0; y < width; y++)
|
|
terrain[x][y] = cell_array[x][y].height;
|
|
return terrain;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
args args = {0};
|
|
|
|
if (parse_args(argc, argv, &args))
|
|
return EXIT_FAILURE;
|
|
|
|
int width = sandpile_width(args.height);
|
|
cell *cells = sandpile_new(width, args.height);
|
|
apply_gravity(width, cells);
|
|
|
|
if (args.png_filename != NULL || args.print_to_stdout) {
|
|
// Convert to int** for easy display:
|
|
int **terrain = cells_to_array(width, cells);
|
|
save_terrain(width, terrain, &args);
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|