This is really cool, nice work (and sorry about your dog).
It reminds me that once upon a time the roguelike dev newsgroup had a "1k (source code) roguelike challenge".
The thread is here [1] and submissions are here in a slightly annoying format [2] but sadly I can't find the entry I remember, which I thought was in C and actually quite playable, if slightly at the mercy of the random number generator to make each new level solvable. Perhaps it was for a later iteration of the same challenge.
I like the old-school notion that I can just copy and paste it into vi and then compile with gcc. It just works. No complicated installation of a runtime or dependencies.
My favorite part is that you get a totally different dungeon each time you run it. It really is a working procedural generator, and not something more like a demo that only produces a single carefully selected result.
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...
I worked a little bit more on it, and thanks to my refactoring I have identified a bug in the original code (which I have intentionally retained in my own code because the point is to produce the same output as the original version, all the way down to and including bug-for-bug compatibility).
Description of bug, which I also posted as a comment on the OP gist:
> Player and other entities will never be placed on the rightmost column of the room floor, nor on the bottom-most row of the room floor. See https://gist.github.com/ctsrc/fef3006e1d728bb7271cff0656eb02... [...]. In my refactored version of your code the bug is explained at the line I linked to in this comment.
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
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.
// 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:
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.
People cope with grief and loss in a lot of different ways. Some dwell, others distract. Similarly, while some folks need to be surrounded by family and friends, others just want to be alone for a while. What's sure is you're never the same afterwards.
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).
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!
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.
This is kind of a pet peeve of mine, but if you post simple, standalone code you really ought to post the compiled executable(s) as well. I don't currently have a C compiler installed on my home Windows box.
If he'd posted an executable, unless it was for exactly your platform, wouldn't you just have complained you didn't currently have a machine running the correct operating system on the correct hardware?
Not at all. Tiny textmode programs do not generally require specific hardware or OS versions. Windows/Mac/Linux should cover the large majority of users. Or you could just do Windows, since Unix machines (including Macs) most likely have gcc installed already. Or you could skip all that and include a Repl.it link, like Yorwba thoughtfully provided in the sibling comment.
If it did require specific hardware or software, that would be different, which is why I specifically said "simple, standalone code."
But you have to use the modified code in the comments above or you will get a :
main.c:8:7: error: variably modified ‘m’ at file scope
=80;i m[H][W];i g(i x) {r rand()%x;}
It reminds me that once upon a time the roguelike dev newsgroup had a "1k (source code) roguelike challenge".
The thread is here [1] and submissions are here in a slightly annoying format [2] but sadly I can't find the entry I remember, which I thought was in C and actually quite playable, if slightly at the mercy of the random number generator to make each new level solvable. Perhaps it was for a later iteration of the same challenge.
[1] https://rec.games.roguelike.development.narkive.com/3tm7xGpn... [2] https://sites.google.com/site/1024brl/