Skip to content

Instantly share code, notes, and snippets.

@brettchalupa
Last active March 24, 2026 02:42
Show Gist options
  • Select an option

  • Save brettchalupa/7a4899c12fbfe74ff16ae1dc5892eb0e to your computer and use it in GitHub Desktop.

Select an option

Save brettchalupa/7a4899c12fbfe74ff16ae1dc5892eb0e to your computer and use it in GitHub Desktop.

Revisions

  1. brettchalupa revised this gist Mar 24, 2026. No changes.
  2. brettchalupa revised this gist Mar 24, 2026. 1 changed file with 78 additions and 0 deletions.
    78 changes: 78 additions & 0 deletions map.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,78 @@
    #ifndef MAP_H
    #define MAP_H

    #include "point.h"
    #include "util.h"

    #define MAX_MAP_WIDTH 44
    #define MAX_MAP_HEIGHT 33
    #define SECTORS_H 4
    #define SECTORS_V 3
    #define SECTOR_WIDTH (MAX_MAP_WIDTH / SECTORS_H)
    #define SECTOR_HEIGHT (MAX_MAP_HEIGHT / SECTORS_V)

    typedef enum {
    kTileWall = 742,
    kTileFloor = 0, // empty
    kTileStairsDown = 297,
    } TileType;

    /**
    * A rectangular area within a sector of a map, with its position and width and
    * height in grid size, not pixel size.
    */
    typedef struct {
    Point pos;
    int w;
    int h;
    } Room;

    /**
    * Generated floor that the player traverses
    */
    typedef struct {
    int width;
    int height;
    TileType tiles[MAX_MAP_WIDTH * MAX_MAP_HEIGHT];
    Room rooms[SECTORS_H * SECTORS_V];
    int roomCount;
    } Map;

    static inline bool isInBounds(Point p, Map *map) {
    return p.x >= 0 && p.x < map->width && p.y >= 0 && p.y < map->height;
    }

    /** Return a random point in the room */
    static inline Point roomRandomPoint(Room *room) {
    return (Point){
    .x = room->pos.x + randInRange(0, room->w - 1),
    .y = room->pos.y + randInRange(0, room->h - 1),
    };
    }

    /** Return a the center point of the room */
    static inline Point roomCenter(Room *room) {
    return (Point){
    .x = room->pos.x + room->w / 2,
    .y = room->pos.y + room->h / 2,
    };
    }

    static inline bool isTileWalkable(TileType tile) {
    return tile == kTileFloor || tile == kTileStairsDown;
    }

    static inline TileType tileAtPos(Map *map, Point pos) {
    return map->tiles[pointToIndex(pos, map->width)];
    }

    /**
    * Draw the map to the screen with the renderer, setting offset with the camera.
    *
    * @param cellSize pixel size of each grid cell (tiles are drawn at this size)
    */
    void drawMap(Map *map, Point camera, int cellSize);
    void generateMap(Map *map);
    void setStairsAtPoint(Map *map, Point p);

    #endif
  3. brettchalupa created this gist Mar 24, 2026.
    144 changes: 144 additions & 0 deletions map.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,144 @@
    #include "map.h"
    #include "platform.h"
    #include "util.h"

    #include <string.h>

    static int pointToTileIndex(Map *map, Point p) { return p.y * map->width + p.x; }

    static void setTileAtPoint(Map *map, Point p, TileType type) {
    map->tiles[pointToTileIndex(map, p)] = type;
    }

    static void carveRooms(Map *map) {
    // carve out rooms
    for (int sectorY = 0; sectorY < SECTORS_V; sectorY++) {
    for (int sectorX = 0; sectorX < SECTORS_H; sectorX++) {
    // skip rooms 10% of the time
    if (percentChance(10))
    continue;

    int roomW = randInRange(SECTOR_WIDTH - 6, SECTOR_WIDTH - 2);
    int roomH = randInRange(SECTOR_HEIGHT - 8, SECTOR_HEIGHT - 4);
    int roomX = randInRange(1, SECTOR_WIDTH - roomW - 1);
    int roomY = randInRange(1, SECTOR_HEIGHT - roomH - 1);
    int sectorOffsetX = sectorX * SECTOR_WIDTH;
    int sectorOffsetY = sectorY * SECTOR_HEIGHT;

    map->rooms[sectorY * SECTORS_H + sectorX] = (Room){
    .pos = {.x = roomX + sectorOffsetX, .y = roomY + sectorOffsetY},
    .w = roomW,
    .h = roomH,
    };
    map->roomCount++;

    for (int y = roomY; y < roomH + roomY; y++) {
    for (int x = roomX; x < roomW + roomX; x++) {
    map->tiles[(sectorOffsetY + y) * map->width + (sectorOffsetX + x)] = kTileFloor;
    }
    }
    }
    }
    }

    // carve horizontal from x1 to x2 at row y
    static void carveH(Map *map, int x1, int x2, int y) {
    int startX = x1 < x2 ? x1 : x2;
    int endX = x1 < x2 ? x2 : x1;
    for (int x = startX; x <= endX; x++) {
    setTileAtPoint(map, (Point){.x = x, .y = y}, kTileFloor);
    }
    }

    // carve vertical from y1 to y2 at column x
    static void carveV(Map *map, int x, int y1, int y2) {
    int startY = y1 < y2 ? y1 : y2;
    int endY = y1 < y2 ? y2 : y1;
    for (int y = startY; y <= endY; y++) {
    setTileAtPoint(map, (Point){.x = x, .y = y}, kTileFloor);
    }
    }

    static void carveCorridors(Map *map) {
    // connect each room to the next existing room to its right
    for (int sy = 0; sy < SECTORS_V; sy++) {
    for (int sx = 0; sx < SECTORS_H; sx++) {
    Room *a = &map->rooms[sy * SECTORS_H + sx];
    if (a->w == 0)
    continue;
    for (int nx = sx + 1; nx < SECTORS_H; nx++) {
    Room *b = &map->rooms[sy * SECTORS_H + nx];
    if (b->w == 0)
    continue;
    Point pa = roomRandomPoint(a);
    Point pb = roomRandomPoint(b);
    carveH(map, pa.x, pb.x, pa.y);
    carveV(map, pb.x, pa.y, pb.y);
    sx = nx - 1;
    break;
    }
    }
    }

    // connect each room to the next existing room below
    for (int sx = 0; sx < SECTORS_H; sx++) {
    for (int sy = 0; sy < SECTORS_V; sy++) {
    Room *a = &map->rooms[sy * SECTORS_H + sx];
    if (a->w == 0)
    continue;
    for (int ny = sy + 1; ny < SECTORS_V; ny++) {
    Room *b = &map->rooms[ny * SECTORS_H + sx];
    if (b->w == 0)
    continue;
    Point pa = roomRandomPoint(a);
    Point pb = roomRandomPoint(b);
    carveV(map, pa.x, pa.y, pb.y);
    carveH(map, pa.x, pb.x, pb.y);
    sy = ny - 1;
    break;
    }
    }
    }
    }

    void generateMap(Map *map) {
    map->roomCount = 0;
    memset(map->rooms, 0, sizeof(map->rooms));
    map->width = MAX_MAP_WIDTH;
    map->height = MAX_MAP_HEIGHT;

    // fill with walls
    for (int i = 0; i < map->width * map->height; i++) {
    map->tiles[i] = kTileWall;
    }

    carveRooms(map);
    carveCorridors(map);
    }

    void drawMap(Map *map, Point camera, int cellSize) {
    int startTileX = camera.x / cellSize;
    int startTileY = camera.y / cellSize;
    int endTileX = (camera.x + screenWidth()) / cellSize + 1;
    int endTileY = (camera.y + screenHeight()) / cellSize + 1;

    if (startTileX < 0)
    startTileX = 0;
    if (startTileY < 0)
    startTileY = 0;
    if (endTileX >= map->width)
    endTileX = map->width - 1;
    if (endTileY >= map->height)
    endTileY = map->height - 1;

    for (int y = startTileY; y <= endTileY; y++) {
    for (int x = startTileX; x <= endTileX; x++) {
    int i = pointToIndex((Point){x, y}, map->width);
    int px = x * cellSize - camera.x;
    int py = y * cellSize - camera.y;
    drawTile(map->tiles[i], px, py, cellSize);
    }
    }
    }

    void setStairsAtPoint(Map *map, Point p) { setTileAtPoint(map, p, kTileStairsDown); }
    79 changes: 79 additions & 0 deletions mapgen.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,79 @@
    #include "raylib.h"
    #define RAYGUI_IMPLEMENTATION
    #include "../../../src/map.h"
    #include "../../../src/platform.h"
    #include "../../vendor/raygui.h"
    #include "raylib.h"
    #include <time.h>

    //------------------------------------------------------------------------------------
    // Program main entry point
    //------------------------------------------------------------------------------------
    int main(void) {
    // Initialization
    //--------------------------------------------------------------------------------------
    SetConfigFlags(FLAG_WINDOW_RESIZABLE);
    InitWindow(1280, 720, "mapgen");
    srand(time(NULL)); // NOLINT(bugprone-random-generator-seed)
    initRenderer("../../Source/images/kenney-1bit-table-16-16.png", 16, 0, 0); // 0,0 = use window size
    Point camera = {.x = 0, .y = 0};
    int cameraSpeed = 16;
    int cellSize = 16;
    static Map map;
    generateMap(&map);

    SetTargetFPS(60);
    //--------------------------------------------------------------------------------------

    // Main game loop
    while (!WindowShouldClose()) // Detect window close button or ESC key
    {
    // Update
    //----------------------------------------------------------------------------------
    if (IsKeyDown(KEY_W))
    camera.y -= cameraSpeed;
    if (IsKeyDown(KEY_S))
    camera.y += cameraSpeed;
    if (IsKeyDown(KEY_A))
    camera.x -= cameraSpeed;
    if (IsKeyDown(KEY_D))
    camera.x += cameraSpeed;
    if (IsKeyPressed(KEY_Q))
    cellSize -= 8;
    if (IsKeyPressed(KEY_E))
    cellSize += 8;

    cellSize = max(cellSize, 8);
    cellSize = min(cellSize, 128);

    if (IsKeyPressed(KEY_R)) {
    char screenshotName[64];
    snprintf(screenshotName, sizeof(screenshotName), "map-%ld.png", (long)time(NULL));
    TakeScreenshot(screenshotName);
    }

    if (IsKeyPressed(KEY_SPACE)) {
    generateMap(&map);
    }
    //----------------------------------------------------------------------------------

    // Draw
    //----------------------------------------------------------------------------------
    BeginDrawing();

    ClearBackground(WHITE);

    drawMap(&map, camera, cellSize);

    EndDrawing();
    //----------------------------------------------------------------------------------
    }

    // De-Initialization
    //--------------------------------------------------------------------------------------
    freeRenderer();
    CloseWindow(); // Close window and OpenGL context
    //--------------------------------------------------------------------------------------

    return 0;
    }