Created
December 8, 2025 07:38
-
-
Save yves-chevallier/acabea2fd6d8da76b7d1f161b4f7ad08 to your computer and use it in GitHub Desktop.
Fire Propagation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <time.h> | |
| #define GRID_SIZE 20 | |
| #define MAX_TEMP 100.0 | |
| #define MIN_IGNITE_TEMP 50.0 | |
| #define FIRE_PROB_AT_100 0.5 | |
| typedef struct { | |
| double cells[GRID_SIZE][GRID_SIZE]; | |
| } Grid; | |
| typedef struct { | |
| int r, g, b; | |
| } RGB; | |
| static double clamp(double value, double min, double max) { | |
| if (value < min) return min; | |
| if (value > max) return max; | |
| return value; | |
| } | |
| // Quadratic curve between 50 (0%) and 100 (50%). | |
| // Using a squared term makes higher temperatures disproportionately more dangerous. | |
| static double ignition_probability(double temp) { | |
| if (temp < MIN_IGNITE_TEMP) return 0.0; | |
| double norm = (temp - MIN_IGNITE_TEMP) / (MAX_TEMP - MIN_IGNITE_TEMP); // 0..1 | |
| double curved = norm * norm; // non-linear ramp-up | |
| return FIRE_PROB_AT_100 * curved; | |
| } | |
| static void read_initial_fires(Grid *grid) { | |
| int x, y; | |
| while (scanf("%d %d", &x, &y) == 2) { | |
| if (x >= 0 && x < GRID_SIZE && y >= 0 && y < GRID_SIZE) { | |
| grid->cells[y][x] = MAX_TEMP; | |
| } | |
| } | |
| } | |
| static void diffuse_heat(const Grid *current, Grid *next) { | |
| memcpy(next, current, sizeof(Grid)); | |
| for (int r = 0; r < GRID_SIZE; ++r) { | |
| for (int c = 0; c < GRID_SIZE; ++c) { | |
| if (current->cells[r][c] <= 0.0) continue; | |
| for (int dr = -1; dr <= 1; ++dr) { | |
| for (int dc = -1; dc <= 1; ++dc) { | |
| if (dr == 0 && dc == 0) continue; | |
| int nr = r + dr; | |
| int nc = c + dc; | |
| if (nr < 0 || nr >= GRID_SIZE || nc < 0 || nc >= GRID_SIZE) continue; | |
| next->cells[nr][nc] = clamp(next->cells[nr][nc] + 1.0, 0.0, MAX_TEMP); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| static void ignite(Grid *grid) { | |
| for (int r = 0; r < GRID_SIZE; ++r) { | |
| for (int c = 0; c < GRID_SIZE; ++c) { | |
| if (grid->cells[r][c] >= MAX_TEMP) { | |
| grid->cells[r][c] = MAX_TEMP; | |
| continue; | |
| } | |
| double p = ignition_probability(grid->cells[r][c]); | |
| double roll = rand() / (double)RAND_MAX; | |
| if (roll < p) { | |
| grid->cells[r][c] = MAX_TEMP; | |
| } | |
| } | |
| } | |
| } | |
| // Truecolor matplotlib-like \"hot\": red ramps to 1 by 0.3, green 0.3->0.6, blue 0.6->1.0. | |
| static RGB pick_color(double temp) { | |
| double x = clamp(temp / MAX_TEMP, 0.0, 1.0); | |
| double r = (x < 0.3) ? (x / 0.3) : 1.0; | |
| double g = (x < 0.3) ? 0.0 : (x < 0.6 ? (x - 0.3) / 0.3 : 1.0); | |
| double b = (x < 0.6) ? 0.0 : (x - 0.6) / 0.4; | |
| RGB rgb = { | |
| .r = (int)(r * 255.0 + 0.5), | |
| .g = (int)(g * 255.0 + 0.5), | |
| .b = (int)(b * 255.0 + 0.5) | |
| }; | |
| return rgb; | |
| } | |
| static void render_simple(const Grid *grid) { | |
| for (int r = 0; r < GRID_SIZE; ++r) { | |
| for (int c = 0; c < GRID_SIZE; ++c) { | |
| printf("%3d ", (int)(grid->cells[r][c] + 0.5)); | |
| } | |
| puts(""); | |
| } | |
| } | |
| static void render_ansi(const Grid *grid) { | |
| for (int r = 0; r < GRID_SIZE; ++r) { | |
| for (int c = 0; c < GRID_SIZE; ++c) { | |
| RGB rgb = pick_color(grid->cells[r][c]); | |
| printf("\x1b[48;2;%d;%d;%dm \x1b[0m", rgb.r, rgb.g, rgb.b); | |
| } | |
| puts(""); | |
| } | |
| } | |
| static void step(const Grid *current, Grid *next) { | |
| diffuse_heat(current, next); | |
| ignite(next); | |
| } | |
| static void usage(const char *prog) { | |
| fprintf(stderr, "Usage: %s [-s steps] [--ansi]\n", prog); | |
| fprintf(stderr, "Provide initial fire coordinates on stdin as 'x y' pairs (0-%d).\n", GRID_SIZE - 1); | |
| } | |
| int main(int argc, char *argv[]) { | |
| int steps = 20; | |
| int use_ansi = 0; | |
| for (int i = 1; i < argc; ++i) { | |
| if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--steps") == 0) { | |
| if (i + 1 >= argc) { | |
| usage(argv[0]); | |
| return 1; | |
| } | |
| steps = atoi(argv[++i]); | |
| } else if (strcmp(argv[i], "--ansi") == 0) { | |
| use_ansi = 1; | |
| } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { | |
| usage(argv[0]); | |
| return 0; | |
| } else { | |
| usage(argv[0]); | |
| return 1; | |
| } | |
| } | |
| srand((unsigned int)time(NULL)); | |
| Grid grid = {0}; | |
| read_initial_fires(&grid); | |
| Grid next = {0}; | |
| for (int t = 0; t < steps; ++t) { | |
| step(&grid, &next); | |
| grid = next; | |
| } | |
| if (use_ansi) { | |
| render_ansi(&grid); | |
| } else { | |
| render_simple(&grid); | |
| } | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment