#include #include #include #include #include #include "maze32_maze.h" #define SEED_COUNT 1024U static maze32_maze_t maze; static maze32_maze_t repeated_maze; static maze32_maze_t tampered_maze; static maze32_maze_workspace_t workspace; static char object_at(const maze32_maze_t* current_maze, maze32_cell_t cell) { const char labels[] = {'1', '2', 'k', 'K', 'e', 'E', 'c', 'C'}; const maze32_cell_t* required[] = {current_maze->starts, current_maze->keys, current_maze->exits, current_maze->compasses}; for (size_t group = 0U; group < 4U; ++group) { for (size_t player = 0U; player < 2U; ++player) { if (required[group][player].x == cell.x && required[group][player].y == cell.y) { return labels[group * 2U + player]; } } } for (size_t pair = 0U; pair < current_maze->config.landmark_pairs; ++pair) { for (size_t side = 0U; side < 2U; ++side) { if (current_maze->landmarks[pair].cells[side].x == cell.x && current_maze->landmarks[pair].cells[side].y == cell.y) { return 'L'; } } } for (size_t pair = 0U; pair < current_maze->config.artifact_pairs; ++pair) { for (size_t side = 0U; side < 2U; ++side) { if (current_maze->artifacts[pair][side].x == cell.x && current_maze->artifacts[pair][side].y == cell.y) { return 'V'; } } } return ' '; } static void print_representative_map(const maze32_maze_t* current_maze) { puts("representative 16x8 map (1/2 starts, k/K keys, e/E exits, c/C compasses):"); for (uint16_t y = 0U; y < current_maze->config.height; ++y) { putchar('+'); for (uint16_t x = 0U; x < current_maze->config.width; ++x) { maze32_cell_t cell = {x, y}; fputs(maze32_maze_has_passage(current_maze, cell, MAZE32_PASSAGE_NORTH) ? " +" : "--+", stdout); } putchar('\n'); putchar('|'); for (uint16_t x = 0U; x < current_maze->config.width; ++x) { maze32_cell_t cell = {x, y}; printf("%c%c", object_at(current_maze, cell), maze32_maze_has_passage(current_maze, cell, MAZE32_PASSAGE_EAST) ? ' ' : '|'); } putchar('\n'); } for (uint16_t x = 0U; x < current_maze->config.width; ++x) { fputs("+--", stdout); } puts("+"); } static void test_invalid_configuration(void) { maze32_maze_config_t config = maze32_maze_default_config(); maze32_maze_report_t report; config.width = 127U; assert(maze32_maze_generate(&maze, &workspace, &config, 1U, &report) == MAZE32_MAZE_INVALID_CONFIG); assert(report.result == MAZE32_MAZE_INVALID_CONFIG); } static void test_bounded_regeneration_failure(void) { maze32_maze_config_t config = maze32_maze_default_config(); maze32_maze_report_t report; config.width = 8U; config.height = 8U; config.extra_cycle_pairs = 64U; config.minimum_exit_separation = 4U; config.maximum_attempts = 3U; assert(maze32_maze_generate(&maze, &workspace, &config, 7U, &report) == MAZE32_MAZE_GENERATION_EXHAUSTED); assert(report.attempts == config.maximum_attempts); assert(report.last_validation_result != MAZE32_MAZE_OK); puts("maze bounded regeneration failure: PASS (3/3 attempts)"); } static void test_large_seed_set(void) { maze32_maze_config_t config = maze32_maze_default_config(); uint16_t minimum_cycles = UINT16_MAX; uint16_t maximum_cycles = 0U; uint16_t minimum_dead_ends = UINT16_MAX; uint16_t maximum_dead_ends = 0U; uint16_t minimum_start_key = UINT16_MAX; uint16_t maximum_start_key = 0U; uint16_t minimum_key_exit = UINT16_MAX; uint16_t maximum_key_exit = 0U; uint16_t minimum_start_exit = UINT16_MAX; uint16_t maximum_start_exit = 0U; uint8_t maximum_attempts = 0U; clock_t maximum_ticks = 0; uint64_t combined_hash = 0U; for (uint32_t seed = 0U; seed < SEED_COUNT; ++seed) { maze32_maze_report_t report; clock_t started = clock(); assert(maze32_maze_generate(&maze, &workspace, &config, seed, &report) == MAZE32_MAZE_OK); clock_t elapsed = clock() - started; if (elapsed > maximum_ticks) { maximum_ticks = elapsed; } assert(report.result == MAZE32_MAZE_OK); assert(report.attempts >= 1U && report.attempts <= config.maximum_attempts); if (report.attempts > maximum_attempts) { maximum_attempts = report.attempts; } assert(report.metrics.reachable_cells == MAZE32_MAZE_MAX_CELLS); assert(report.metrics.cycle_count > 0U); assert(report.metrics.cross_half_connections >= 4U); assert(report.metrics.dead_end_count > 0U); assert(report.metrics.start_to_key[0] == report.metrics.start_to_key[1]); assert(report.metrics.key_to_exit[0] == report.metrics.key_to_exit[1]); assert(report.metrics.start_to_exit[0] == report.metrics.start_to_exit[1]); assert(report.metrics.start_to_compass[0] == report.metrics.start_to_compass[1]); assert(report.metrics.landmark_distance_total[0] == report.metrics.landmark_distance_total[1]); assert(report.metrics.artifact_distance_total[0] == report.metrics.artifact_distance_total[1]); assert(report.metrics.start_to_key[0] >= report.metrics.key_to_exit[0]); uint64_t first_hash = maze32_maze_hash(&maze); maze32_maze_report_t repeated_report; assert(maze32_maze_generate(&repeated_maze, &workspace, &config, seed, &repeated_report) == MAZE32_MAZE_OK); assert(first_hash == maze32_maze_hash(&repeated_maze)); combined_hash ^= first_hash + seed * UINT64_C(0x9e3779b97f4a7c15); if (report.metrics.cycle_count < minimum_cycles) { minimum_cycles = report.metrics.cycle_count; } if (report.metrics.cycle_count > maximum_cycles) { maximum_cycles = report.metrics.cycle_count; } if (report.metrics.dead_end_count < minimum_dead_ends) { minimum_dead_ends = report.metrics.dead_end_count; } if (report.metrics.dead_end_count > maximum_dead_ends) { maximum_dead_ends = report.metrics.dead_end_count; } if (report.metrics.start_to_key[0] < minimum_start_key) { minimum_start_key = report.metrics.start_to_key[0]; } if (report.metrics.start_to_key[0] > maximum_start_key) { maximum_start_key = report.metrics.start_to_key[0]; } if (report.metrics.key_to_exit[0] < minimum_key_exit) { minimum_key_exit = report.metrics.key_to_exit[0]; } if (report.metrics.key_to_exit[0] > maximum_key_exit) { maximum_key_exit = report.metrics.key_to_exit[0]; } if (report.metrics.start_to_exit[0] < minimum_start_exit) { minimum_start_exit = report.metrics.start_to_exit[0]; } if (report.metrics.start_to_exit[0] > maximum_start_exit) { maximum_start_exit = report.metrics.start_to_exit[0]; } } double maximum_ms = (double)maximum_ticks * 1000.0 / CLOCKS_PER_SEC; printf("maze seed sweep: PASS (%u seeds, combined hash=%016" PRIx64 ")\n", SEED_COUNT, combined_hash); printf("maze metrics: cycles=%u..%u, dead ends=%u..%u, start-key=%u..%u, " "key-exit=%u..%u, start-exit=%u..%u, max attempts=%u\n", minimum_cycles, maximum_cycles, minimum_dead_ends, maximum_dead_ends, minimum_start_key, maximum_start_key, minimum_key_exit, maximum_key_exit, minimum_start_exit, maximum_start_exit, maximum_attempts); printf("maze host runtime: maximum %.3f ms per generated map\n", maximum_ms); } static void test_validator_rejects_corruption(void) { maze32_maze_config_t config = maze32_maze_default_config(); maze32_maze_report_t report; maze32_maze_metrics_t metrics; assert(maze32_maze_generate(&maze, &workspace, &config, 42U, &report) == MAZE32_MAZE_OK); tampered_maze = maze; tampered_maze.passages[0] ^= MAZE32_PASSAGE_EAST; assert(maze32_maze_validate(&tampered_maze, &workspace, &metrics) == MAZE32_MAZE_ASYMMETRIC); tampered_maze = maze; tampered_maze.keys[0] = tampered_maze.starts[0]; tampered_maze.keys[1] = tampered_maze.starts[1]; assert(maze32_maze_validate(&tampered_maze, &workspace, &metrics) == MAZE32_MAZE_PLACEMENT_COLLISION); puts("maze corruption rejection: PASS"); } static void test_memory_and_representative_map(void) { size_t fixed_bytes = sizeof(maze32_maze_t) + sizeof(maze32_maze_workspace_t); assert(fixed_bytes <= MAZE32_MAZE_RAM_BUDGET_BYTES); printf("maze fixed memory: PASS (%zu/%u bytes, no heap API)\n", fixed_bytes, MAZE32_MAZE_RAM_BUDGET_BYTES); maze32_maze_config_t config = maze32_maze_default_config(); config.width = 16U; config.height = 8U; config.cross_half_pairs = 2U; config.extra_cycle_pairs = 4U; config.minimum_exit_separation = 4U; config.landmark_pairs = 2U; config.artifact_pairs = 2U; maze32_maze_report_t report; assert(maze32_maze_generate(&maze, &workspace, &config, UINT32_C(0x5a17), &report) == MAZE32_MAZE_OK); printf("representative map hash: %016" PRIx64 "\n", maze32_maze_hash(&maze)); print_representative_map(&maze); } int main(void) { test_invalid_configuration(); test_bounded_regeneration_failure(); test_large_seed_set(); test_validator_rejects_corruption(); test_memory_and_representative_map(); puts("maze generation tests: PASS"); return 0; }