
A random dungeon generator in C, small enough to fit on a business card - DyslexicAtheist
https://gist.github.com/munificent/b1bcd969063da3e6c298be070a22b604
======
yorwba
Previously:
[https://news.ycombinator.com/item?id=19290215](https://news.ycombinator.com/item?id=19290215)

------
alien1993
He's the same guy that wrote the book Game Programming Patterns.
[http://gameprogrammingpatterns.com/](http://gameprogrammingpatterns.com/)

~~~
jameskilton
And is currently writing the online book Crafting Interpreters
[http://www.craftinginterpreters.com/](http://www.craftinginterpreters.com/)

------
grownseed
Added a javascript version, for those who want to try it out without compiling
anything:
[https://gist.github.com/munificent/b1bcd969063da3e6c298be070...](https://gist.github.com/munificent/b1bcd969063da3e6c298be070a22b604#gistcomment-2854606).

Online version:
[https://jsfiddle.net/dfu7ws69/](https://jsfiddle.net/dfu7ws69/)

------
yourfate
Very cool.

To try and understand it I reformated it to make it somewhat readable:

[https://gitlab.com/snippets/1832264](https://gitlab.com/snippets/1832264)

~~~
homarp
[https://gist.github.com/munificent/ce8f7a9e6b09938ca8d2d43fa...](https://gist.github.com/munificent/ce8f7a9e6b09938ca8d2d43fa62f9864)
(from the author)

[https://gist.github.com/femto113/28f69626acddc70a002ecead0b3...](https://gist.github.com/femto113/28f69626acddc70a002ecead0b311da4)
(from
[https://news.ycombinator.com/item?id=19290841](https://news.ycombinator.com/item?id=19290841)
)

~~~
codetrotter
Hopefully without sounding like I am trying to boast or anything... I find the
difference in readability and clarity _dramatic_ between those two on one hand
and my own deobfuscated, refactored and commented version of the OP.
[https://gist.github.com/ctsrc/fef3006e1d728bb7271cff0656eb02...](https://gist.github.com/ctsrc/fef3006e1d728bb7271cff0656eb0280#file-
dungeon-c)

~~~
homarp
regarding your comment:

>// Probably plus means a locked door, and single quote means unlocked, or the
other way around.

[http://angband.oook.cz/stuff/manual.txt](http://angband.oook.cz/stuff/manual.txt)
gives

' An open /broken door

\+ A closed door

$ Gold or gems

A Angelic being

~ Light sources, Tools, Chests, Junk, Sticks, Skeletons, etc

~~~
codetrotter
Nice, thanks. I've updated the comments with the information you provided here
:)

------
airstrike
Backported this to run on MS Visual Studio 2008 in case anyone is equally
constrained and still wants to give it a go. Did it based on @Joker-vD's
deobfuscated gist

[https://gist.github.com/airstrike/66e0152e75c3a81fd1496bfbf5...](https://gist.github.com/airstrike/66e0152e75c3a81fd1496bfbf590105a)

------
munificent
Author here. I love all of the deobfuscations. Usually, when I put code
online, I try to make it as _clear_ as possible. It's really interesting to
see that going the other direction actually makes it a more interactive
experience for the reader.

This one is great:
[https://gist.github.com/airstrike/66e0152e75c3a81fd1496bfbf5...](https://gist.github.com/airstrike/66e0152e75c3a81fd1496bfbf590105a)

A couple of remarks:

    
    
        // The door should not be created in the cave's corner or over
        // another door, or in another cave's corner. It's impossible
        // to make a cave without a door, because randInt(1) always
        // returns 0.
        if (atWallButNotAtCorner && FIELD[y][x] == TILE_WALL) {
            doorCounter++;
            if (randInt(doorCounter) == 0) {
                doorX = x;
                doorY = y;
            }
        }
    

The randInt() part here is pretty confusing. Here's the intent of the code. It
picks a random boundary for the new room. Then it walks over the edges and
finds every tile where the room's wall overlaps the wall of an existing room.
Those are candidates where a door can be placed to connect this room to the
existing one.

If no candidates are found, the room is discarded. This ensures the dungeon is
always connected.

If there are _multiple_ candidates, we only need to pick one. We want to pick
one randomly because otherwise you'd get obviously biased choices where the
door always appeared at the left-most edge between two rooms or something. The
obvious way to do that is to build a list of the candidate coordinates and
then choose a random element from the list.

But that's a lot of code. Instead, I use Algorithm R [0]. It's a streaming
algorithm for selecting a random item as you walk the set of items being
sampled. The idea is that you keep a running winner. Each new element, you
have a random chance of replacing the winner. As the number of elements
visited increases, the chances of replacing the winner decreases. So the first
element has a 1/1 chance of being the winner. The second has a 1/2 chance of
replacing the winner, the third 1/3, etc.

    
    
        // If the cave's walls were made completely out of corners
        // and doors, don't make such a cave
        if (doorCounter == 0) { return; }
    

This case actually means the new room didn't share a wall with any existing
room.

    
    
        // We need to somehow record corners of all caves to check
        // for intersections later, so we use a special tile for it
        FIELD[y][x] = atCorner
            ? TILE_CORNER
            : (atWallButNotAtCorner ? TILE_WALL : TILE_FLOOR);
    

For a room to connect to an existing one, they need to share a tile on their
actual sides, like:

    
    
        #####
        #...#
        #...#####
        #...X...#
        #####...#
            #...#
            #####
    

That's leaves at least one tile where we can place a door. If only the corners
overlap, there may not be enough room to connect them:

    
    
        #####
        #...#
        #...#
        #...#####
        #####...# ???
            #...#
            #...#
            #####
    
        #####
        #...#
        #...#
        #...#
        ######### ???
            #...#
            #...#
            #...#
            #####
    

So, during room placement, the corners are not treated as part of the room:

    
    
         ###
        #...#
        #...####
        #...X...#
         ####...#
            #...#
             ### 
    
         ###
        #...#
        #...#
        #...####
         ####...# ???
            #...#
            #...#
             ### 
    
         ###
        #...#
        #...#
        #...#
         ### ###  ???
            #...#
            #...#
            #...#
             ### 
    

But, when rendering the rooms, I want them to look rectangular. So the special
"!" means "render like a wall, but don't act like a wall".

    
    
        // 1d6 of entities total, 25% chance of gold, 75% of a mob.
        // Mob letters range from 'A' to '~', inclusive
    

Technically, the "mob" case includes all of the non-"$" treasure too.
Punctuation characters are in that character range as well and represent
items.

Otherwise, the comments are all spot on. It's really gratifying seeing people
figure this all out.

[0]:
[https://en.wikipedia.org/wiki/Reservoir_sampling#Algorithm_R](https://en.wikipedia.org/wiki/Reservoir_sampling#Algorithm_R)

------
packetpirate
Fucking black magic obfuscation. Is it possible to learn this power?

I want to see someone try to rewrite C++'s entire syntax in the form of
another language (I just have to assume it has already been done.)

~~~
cdata
I'm no expert, but in this case there are a few strategic definitions that
help a lot to both conserve space and obfuscate:

    
    
      #define r return
      #define l(a, b, c, d) for (i y = a; y < b; y++) for (int x = c; x < d; x++)
      typedef int i;

~~~
munificent
My goal was less to obfuscate than it was to compress the code. I kept it as
clear as I could within the limits of trying to get the code really small.
There weren't any deliberate obfuscations like using misleading variable
names.

The "l" macro cut the code down significantly and is one of the tricks I'm
most proud of.

"r" and "i" aren't that impactful in terms of length. Their main benefit is
that they make it easier to split the code where I need to in order to render
the big ASCII art "@" sign. That's a lot harder when you have multiple-letter
identifiers that can't be split in the middle.

------
strenholme
I have written an obfuscated cryptographically secure random password
generator:

[https://github.com/samboy/rg32hash/blob/master/C/tinyrg32.c](https://github.com/samboy/rg32hash/blob/master/C/tinyrg32.c)

The file has test cases, documentation:

[https://github.com/samboy/rg32hash/blob/master/C/tinyrg32.md](https://github.com/samboy/rg32hash/blob/master/C/tinyrg32.md)

and I even have a 12-page de-obfuscation of an earlier version of this
program:

[https://github.com/samboy/rg32hash/blob/master/C/nanorg32.md](https://github.com/samboy/rg32hash/blob/master/C/nanorg32.md)

(I have since then come up more size optimizations)

------
donarb
Here's my quick port to Swift. It can run in a playground.

[https://gist.github.com/donarb/1502a12cb16fa74f4fd2e295867da...](https://gist.github.com/donarb/1502a12cb16fa74f4fd2e295867da253)

------
holografix
Does anyone know of a dungeon generated tool that takes a few, or many inputs?
Type of creatures, terrain, season, presence of water, etc etc?

------
choeger
I really hope Ginny was a dog or a cat or some other pet. Terrifying thought,
it might have been a child.

~~~
munificent
She was my dog: [https://imgur.com/a/p1QQosK](https://imgur.com/a/p1QQosK)

~~~
ben174
Sorry for your loss :(

------
ioata
here is one in python(a very very basic version, big enough to fill an A4 size
sheet :D ) [https://github.com/anekix/dungeon-
generator](https://github.com/anekix/dungeon-generator)

------
Aardappel
Port to Lobster:
[https://gist.github.com/munificent/b1bcd969063da3e6c298be070...](https://gist.github.com/munificent/b1bcd969063da3e6c298be070a22b604#gistcomment-2854781)

------
flend
Why not go for a full roguelike game in 1KB: [https://wasyl.eu/games/another-
visit-in-hell.html](https://wasyl.eu/games/another-visit-in-hell.html)

------
hawkeye424
...still waiting for the google maps to for instance counterstrike map
generator that must not fit on a business card...

------
metalliqaz
So this creates random maps? What does one do with the output of this thing?

~~~
fb03
Well i'm not sure it is meant to be 'used' as input to anything else.

It's nice as it is: just someone having fun coding with self-imposed
constraints (in this case: doing something interesting with a really small
card/code size).

Same as the business card raytracer :)

------
jackbrookes
My favourite "world" generator one-liner (two liner in python):

    
    
      import random as r
      print(''.join([r.choice(["/", "\\"]) for _ in range(9999)]))
    

Output:

    
    
      //\\/\//\\/\\\\/\\\/\//\\\\/\\/\\/\///\\\\///\\/\//\\\\/\\/\ 
      /\\\\/////\\\\\/\\\\////\/\//////\////\\\//////\\\\\/\//\\\/
      //\\\\\\/\/\\//\/\\\\///\\/\\//\\///\\/\/\\//\/\\/\///\\\\//
      \\/\//\\\//////\/\/\/\\/\\//\/\///\\///\/\////\//\/\\\/\/\//
      \\\\\\\\\/\//\\\//\\\/\\/\\\\\/\\\\\///\/\\/\\\\//\\/\\/\///
      //\\/\//////////\\//\\\////\\/////\/\/\\\\\\\\///\/\\/\///\/
      \///\//\\//\\\/\/\\\\\//\\\\\\//\/\\//\\\/\/\/////\/\//\/\//
      /\\\\//\\\\/\\///\\\\/\\\/////\\\\\\//\///\//\\////\//\/\\\/
      /\\/\\\//\/\/\//\//\//\\\//\\\\\\\/\\\/\\\\/\\///\/\/\//\///
      \\/\\/////\/\\\///\\\/\\/\//\/\/\/\\\//\\\\\\\\/////\\/\\/\/
      //\\\\\\\\/\\/////\\/\\\\\///\/\\/\\\/\\//\\\\\\/\\/\\\/////
      \\//\\\\/\\/\\///\///\/\\\/\/\/////\//\\//\/\/\/\/\/\/\\/\//
      \//\\\\\\///\/\\/\\\\/\\\/\/\/\/\//\////\/////\\\/\/\\/\\\\/
      //\\\/\\//\\/\\/\/\\////\/\\//\/\/\///\//////\\///////\/\\//
      \/\////\/\//\\\\//\\/\\/\\\\/\/////\///\/\\/\///\\\\/\\\//\/
      \///\\/\\/\\\\\\/\\\\\\\/\\\///\\\/\\///\\\\//\/\/\/\\//\/\\
      /\\\////\/\//\/////\\///\\//\/\\\/\/\\\/\/\\\\\////\\/\\////

~~~
schoen
This is inspired by this classic BASIC program, which is the inspiration for a
whole book:

[https://10print.org/](https://10print.org/)

(Note that this version of the original is half the size of this Python
adaptation!)

~~~
schoen
Also, I guess you didn't sign up for a code golf session, but this version is
11 bytes shorter:

    
    
       import random
       print(''.join(random.choice("/\\") for _ in "x"*9999))
    

I was surprised to discover that "import random\nrandom.choice", "import
random as r\nrandom.choice", and "__import__("random").choice" are all exactly
27 bytes, so no byte count is saved by preferring any of these forms over
another!

------
volkk
granted that it's probably safe, but seeing a screenshot in the comments of
someone running this code with no clue as to what it actually does under an
"admin" user is kind of funny.

