init
This commit is contained in:
commit
e45f121fb9
89 changed files with 336069 additions and 0 deletions
214
src/genesis_main.c
Normal file
214
src/genesis_main.c
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gen/grid.h"
|
||||
#include "gen/rng.h"
|
||||
#include "gen/room_types.h"
|
||||
#include "gen/events.h"
|
||||
#include "gen/accretion.h"
|
||||
#include "gen/loops.h"
|
||||
#include "gen/lakes.h"
|
||||
#include "gen/walls.h"
|
||||
#include "gen/stairs.h"
|
||||
#include "gen/chokepoints.h"
|
||||
#include "gen/machines.h"
|
||||
#include "gen/dijkstra.h"
|
||||
#include "json_emit.h"
|
||||
|
||||
static int g_verbose = 0;
|
||||
static int g_quiet = 0;
|
||||
static int g_mark_unreached = 0;
|
||||
static int g_emit_json = 0;
|
||||
|
||||
static void print_ascii(grid_t g) {
|
||||
dist_map_t dist;
|
||||
if (g_mark_unreached) {
|
||||
int sx = -1, sy = -1;
|
||||
for (int y = 0; y < DROWS && sx < 0; y++)
|
||||
for (int x = 0; x < DCOLS && sx < 0; x++) {
|
||||
uint8_t t = g[y][x].terrain;
|
||||
if (t == T_FLOOR || t == T_CORRIDOR || t == T_DOOR || t == T_BRIDGE) {
|
||||
sx = x; sy = y;
|
||||
}
|
||||
}
|
||||
if (sx >= 0) bfs_from(g, sx, sy, dist, -1, -1);
|
||||
}
|
||||
for (int y = 0; y < DROWS; y++) {
|
||||
for (int x = 0; x < DCOLS; x++) {
|
||||
char ch;
|
||||
uint8_t t = g[y][x].terrain;
|
||||
int passable = (t == T_FLOOR || t == T_CORRIDOR || t == T_DOOR || t == T_BRIDGE);
|
||||
if (g_mark_unreached && passable && dist[y][x] == DIST_UNREACHABLE) {
|
||||
ch = '!';
|
||||
} else switch (t) {
|
||||
case T_FLOOR: ch = (g[y][x].flags & F_WREATH) ? ';' : '.'; break;
|
||||
case T_CORRIDOR: ch = ','; break;
|
||||
case T_DOOR: ch = '+'; break;
|
||||
case T_WALL: ch = (g[y][x].flags & F_WREATH) ? ':' : '#'; break;
|
||||
case T_LIQUID:
|
||||
switch (g[y][x].liquid) {
|
||||
case L_LAVA: ch = '^'; break;
|
||||
case L_CHASM: ch = 'v'; break;
|
||||
case L_BRIMSTONE: ch = '%'; break;
|
||||
case L_WATER:
|
||||
default: ch = '~'; break;
|
||||
}
|
||||
break;
|
||||
case T_BRIDGE: ch = '='; break;
|
||||
case T_STAIRS_UP: ch = '<'; break;
|
||||
case T_STAIRS_DOWN: ch = '>'; break;
|
||||
default: ch = ' '; break;
|
||||
}
|
||||
putchar(ch);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
static void stderr_sink(void *ctx, const gen_event_t *ev) {
|
||||
(void)ctx;
|
||||
if (!g_verbose) return;
|
||||
fprintf(stderr, "[ev %4u] %s\n",
|
||||
ev->step, event_kind_name((event_kind_t)ev->kind));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int counts[EV_KIND_COUNT];
|
||||
} sink_ctx_t;
|
||||
|
||||
static void counting_sink(void *ctx, const gen_event_t *ev) {
|
||||
sink_ctx_t *s = (sink_ctx_t *)ctx;
|
||||
s->counts[ev->kind]++;
|
||||
if (g_verbose) stderr_sink(NULL, ev);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int rooms, loops, lakes_proposed, lakes_rejected, lakes_placed, bridges;
|
||||
int machines_proposed, machines_placed;
|
||||
int floor_cells;
|
||||
int connected;
|
||||
int unreached;
|
||||
} run_stats_t;
|
||||
|
||||
static void run_once(uint64_t seed, int depth, grid_t *gp, run_stats_t *out, int verify) {
|
||||
cell_t (*g)[DCOLS] = *gp;
|
||||
rng_t rng;
|
||||
rng_seed(&rng, seed);
|
||||
grid_fill(g, T_NOTHING);
|
||||
|
||||
sink_ctx_t sctx = {0};
|
||||
event_sink_t sink = { .fn = counting_sink, .ctx = &sctx, .step = 0 };
|
||||
|
||||
event_emit(&sink, EV_GEN_BEGIN, NULL, 0);
|
||||
|
||||
accrete_rooms(g, &rng, &sink);
|
||||
if (verify && !is_connected(g, NULL))
|
||||
fprintf(stderr, "!! seed %llu: disconnected after accretion\n", (unsigned long long)seed);
|
||||
add_loops(g, &sink);
|
||||
if (verify && !is_connected(g, NULL))
|
||||
fprintf(stderr, "!! seed %llu: disconnected after loops\n", (unsigned long long)seed);
|
||||
place_lakes(g, &rng, &sink, depth);
|
||||
if (verify && !is_connected(g, NULL))
|
||||
fprintf(stderr, "!! seed %llu: disconnected after lakes\n", (unsigned long long)seed);
|
||||
apply_wreaths(g, &sink);
|
||||
place_bridges(g, &sink);
|
||||
finish_walls(g, &sink);
|
||||
place_stairs(g, &sink);
|
||||
|
||||
/* Chokepoint analysis + machine placement. */
|
||||
static choke_map_t choke;
|
||||
static choke_flag_t is_choke, is_gate;
|
||||
compute_chokepoints(g, &sink, choke, is_choke, is_gate);
|
||||
place_machines(g, &rng, depth, &sink, choke, is_gate);
|
||||
|
||||
event_emit(&sink, EV_GEN_END, NULL, 0);
|
||||
|
||||
out->rooms = sctx.counts[EV_ROOM_PLACED];
|
||||
out->loops = sctx.counts[EV_LOOP_ACCEPTED];
|
||||
out->lakes_proposed = sctx.counts[EV_LAKE_PROPOSED];
|
||||
out->lakes_rejected = sctx.counts[EV_LAKE_REJECTED_IMPASSABLE];
|
||||
out->lakes_placed = sctx.counts[EV_LAKE_PLACED];
|
||||
out->bridges = sctx.counts[EV_BRIDGE_PLACED];
|
||||
out->machines_proposed = sctx.counts[EV_MACHINE_PROPOSED];
|
||||
out->machines_placed = sctx.counts[EV_MACHINE_ACCEPTED];
|
||||
out->floor_cells = grid_count_floor(g);
|
||||
out->connected = is_connected(g, &out->unreached);
|
||||
}
|
||||
|
||||
static void print_summary(uint64_t seed, int depth, const run_stats_t *s) {
|
||||
fprintf(stderr,
|
||||
"seed=%llu depth=%d rooms=%d loops=%d "
|
||||
"lakes_proposed=%d lakes_rejected=%d lakes_placed=%d "
|
||||
"bridges=%d machines=%d/%d floor=%d connected=%d unreached=%d\n",
|
||||
(unsigned long long)seed, depth,
|
||||
s->rooms, s->loops,
|
||||
s->lakes_proposed, s->lakes_rejected, s->lakes_placed,
|
||||
s->bridges, s->machines_placed, s->machines_proposed,
|
||||
s->floor_cells, s->connected, s->unreached);
|
||||
}
|
||||
|
||||
static int cmd_stress(int n, uint64_t base_seed, int depth) {
|
||||
grid_t g;
|
||||
int failures = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
uint64_t seed = base_seed + (uint64_t)i;
|
||||
run_stats_t s = {0};
|
||||
run_once(seed, depth, &g, &s, 0);
|
||||
if (!s.connected) {
|
||||
fprintf(stderr, "FAIL seed=%llu unreached=%d\n",
|
||||
(unsigned long long)seed, s.unreached);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "stress: %d seeds, %d failures\n", n, failures);
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint64_t seed = 1234;
|
||||
int depth = 1;
|
||||
int verify = 0;
|
||||
int stress_n = 0;
|
||||
uint64_t stress_base = 1;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--seed") && i + 1 < argc) {
|
||||
seed = strtoull(argv[++i], NULL, 10);
|
||||
} else if (!strcmp(argv[i], "--depth") && i + 1 < argc) {
|
||||
depth = atoi(argv[++i]);
|
||||
if (depth < 1) depth = 1;
|
||||
if (depth > 26) depth = 26;
|
||||
} else if (!strcmp(argv[i], "--verbose")) {
|
||||
g_verbose = 1;
|
||||
} else if (!strcmp(argv[i], "--quiet")) {
|
||||
g_quiet = 1;
|
||||
} else if (!strcmp(argv[i], "--verify")) {
|
||||
verify = 1;
|
||||
} else if (!strcmp(argv[i], "--stress") && i + 1 < argc) {
|
||||
stress_n = atoi(argv[++i]);
|
||||
} else if (!strcmp(argv[i], "--stress-base") && i + 1 < argc) {
|
||||
stress_base = strtoull(argv[++i], NULL, 10);
|
||||
} else if (!strcmp(argv[i], "--mark-unreached")) {
|
||||
g_mark_unreached = 1;
|
||||
} else if (!strcmp(argv[i], "--emit=json") || !strcmp(argv[i], "--json")) {
|
||||
g_emit_json = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (stress_n > 0) return cmd_stress(stress_n, stress_base, depth);
|
||||
|
||||
grid_t g;
|
||||
run_stats_t s = {0};
|
||||
run_once(seed, depth, &g, &s, verify);
|
||||
if (g_emit_json) {
|
||||
emit_grid_json(g, seed, depth);
|
||||
if (verify && !s.connected) return 1;
|
||||
return 0;
|
||||
}
|
||||
print_summary(seed, depth, &s);
|
||||
if (!g_quiet) print_ascii(g);
|
||||
|
||||
if (verify && !s.connected) return 1;
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue