Rooms have traps. We have established that. All well and good. But trap-layers do not sprinkle their work around willy-nilly. A seasoned engineer would place his camouflaged pits, poison needle traps and swinging blocks close to the obvious points of interest, namely (a) the treasure and (b) the way out.
So far, when creating a room, we have allocated the trap (or traps) to random tiles. From now on, the closer a tile is to a treasure tile or an exit tile, the more likely it is to be trapped. Here. This diagram should clear things up.
In the digram above, the probability of a tile being trapped is shown using colour. The treasure tile and the exit tile have a high chance of being trapped. The probability of a tile being trapped decreases the further you get away from these two ‘hot areas’.
How does this affect the trap placement algorithm? I am glad you asked. If you look at the diagram above, you will see that there are 5 different levels of trap likelihood, as follows:
Now, lets attach some numbers to these 5 different levels, like so:
Very high: 1 in 5 tiles are trapped (i.e. probability of 0.2)
High: 1 in 10 tiles are trapped (i.e. probability of 0.1)
Medium: 1 in 20 tiles are trapped (i.e. probability of 0.05)
Low: 1 in 40 tiles are trapped (i.e. probability of 0.025)
Very Low: 1 in 80 tiles are trapped (i.e. probability of 0.0125)
How do we use these trap likelihood levels? I am glad you asked. Assume we have a room with 100 tiles. If every tile in this room has an equal chance of being trapped, we can do this:
Place the 100 Tile objects in a list.
Generate a random number between 0 and 99.
Select the Tile object whose index matches the random number
When the tiles in the room have an unequal chance of being trapped, we must modify this slightly. We do this by rigging the lottery in favour of the higher likelihood tiles. I will write the code tomorrow, but basically this means I would clone the Tile with a very high likelihood of being trapped, giving it 16 entries in the lottery. Tiles with a high chance would get cloned 8 times, and so on. It will work out, I promise 🙂
I am trying to write a pithy description of the game for the player’s guide. This is what I have so far:
Rogue Code is a programming game in which you script the behaviour of an adventurer who is raiding a dungeon.
The dungeon has many rooms. These rooms contain treasure, traps and guards. The adventurer wants to steal the treasure while avoiding the guards and traps.
Your adventurer progresses through the dungeon one room at a time. Each time the adventurer beats a room, the next room is unlocked.
A room always has the same contents (e.g. one guard, one trap, one treasure chest) but the location of those contents changes each time the room is created.
You prepare a rogue for a dungeon raid by writing code. Starting with a basic template, you will customise various aspects of the adventurer’s behaviour.
Once your rogue is complete, you can release him into the first room. Thereafter, the action is turn based. In his turn, the adventurer moves, fights and steals, according to the instructions in your programming. Then the environment reacts (e.g. a trap detonates, a guard moves to attack etc.)
Turn based action continues until the rogue has stolen the treasure and exited the room (thereby beating it) or he is dead.
If the rogue beats the room, he is automatically released into the next room.
Step 8 repeats until the rogue dies.
The source code for the rogue cannot be changed after the first room (i.e. no room specific ‘tuning’).
The final score for a rogue is the number of rooms he can beat before he is killed.
The rogue must raid the dungeon multiple times. This mitigates the potential unfairness that randomising room contents could introduce (e.g. you might start your raid right next to an angry guard!).
That brings us to another game rule. (3) At the start of the game, one or more guards are placed on random tiles in the room. When picking random tiles for the guards, all tiles are legal. The guard (or guards) can be placed on
An edge tile
A non-edge tile
A tile containing the exit door
A tile containing another Guard.
A tile containing treasure.
A tile containing a trap (the guard knows how to avoid it, so it will not trigger)
A tile containing the Rogue!
Our example has just one guard. More guards could be added to increase the difficulty of the room.
Points (4) and (7) uncover an important design decision, namely, that more that one character can inhabit a given tile at a given time. Characters effectively stack. There is no limit to how many of them can be on one tile at a given time. Of course, there are consequences to this sort of proximity (i.e. combat and death). More on this later.
Let’s finish off the set up. (4) At the start of the game, one or more treasures are placed on random tiles in the room. In this context, a random tile is any tile in the room except the tile currently occupied by the Rogue (no free gifts, right?). Accordingly, the treasure must be placed after the rogue has been positioned. Our example has just one treasure tile. More complicated rooms could have lots of treasure.
Finally, we get to traps. (5) At the start of the game, one or more traps are placed on random tiles in the room. In this context, a random tile is any tile in the room except the tile currently occupied by the Rogue. His/her starting tile is assumed to be safe. Therefore, the traps must be placed after the rogue has been positioned. However, traps are more likely to be found near treasure tiles (trap allocation algorithm to follow). Therefore traps must be placed after treasure. Our example has just one trap. More complicated rooms could have multiple traps.
So before I dive in and start writing code, let’s get some theory worked out. Let’s use a simple HTML table to represent the room, with the following notation for fixtures and fittings:
R stands for Rogue
E stands for exit
T stands for trap
G stands for guard
X stands for trap (x marks the stop right)
So, this is a room containing 36 tiles, set out in a 6 x 6 formation. At the start of the run, the Rogue is positioned at (4, 5). These (x, y) coordinates assume that the top left cell in the table is (0,0), and the cell at the bottom right of the table is (5,5). Game rule 1 – At the start of the game, the Rogue is placed on a random tile on the edge of a room. In the room above, there are only 20 legal start positions for the Rogue. All of the legal tiles match one of these four patterns:
Thus, for any square/rectangular room comprised of h x w tiles, there will be (2h + 2w)-2 edge tiles which can be used for the Rogue’s start position.
A similar restriction applies to the exit. Game rule 2 – At the start of the game, the exit is positioned on a random tile on the edge of a room. An exit in the middle of a room doesn’t make sense, right?
After a little playing around, I decided to stick with C# for the implementation. I want to do this pretty fast, and I don’t want to learn a new language as I go. I have dumped some code into a GitHub repo. It’s very basic. It creates a command line representation of a room (R = Rogue!). More to follow.
By the way, the Visual Studio extension for GitHub works very nicely. Microsoft are really playing well with others at the moment.
Here is another trait of mine I could do without. Whenever I embark on a new project, I have a tenancy to make it really complicated, really quickly. I have a recent example of this. Last night I was sketching out the range of actions a rogue can take each turn. I immediately made it far too complicated. I had three levels of movement (sneak, walk, run) each with a different noise modifier. I had a separate ‘detect traps’ action. I was making it pretty tough for myself. In the end, I simplified things down to the following 5 actions:
Sneak – For one action point, a rogue can sneak one tile in any direction. This includes diagonal movement. When sneaking, a rogue has a medium chance of detecting traps.
Move – For one action point, a rogue can move two tiles in any direction. This includes diagonal movement. A walk action can combine directions (e.g. up and then left). When walking, a rogue has a low chance of detecting traps
Melee attack – For one action point, the rogue can melee attack a guard in the 8 tiles around him. Melee attacks made by the rogue have a high chance of hitting the target. A melee attack that hits will always kill the target.
Ranged attack – For one action point, the rogue can attack any guard within 5 tiles (in any direction). Ranged attacks made by the rogue have a variable chance of hitting the target (the further away the target, the lower the chance it will hit). A ranged attack that hits will always kill the target.
Steal – For one action point, the rogue can steal any treasure in the 8 tiles around him. There is no limit to the amount of treasure a rogue can carry. The reassure is assumed to be light and portable.
This should be a lot easier to implement. Some of the other stuff I thought about (‘fog of war’ type vision, effect of noise on environment etc.) can be introduced later, when I get the basic stuff working. It’s a clear case of KISS with maybe some YAGNI thrown in for good measure.
Anyway, the next thing I am going to do is get set up for Python development. I thought it might be a good idea to use Rogue Code as an opportunity to work in another language. So I need to download the latest Python release and a good editor. I have picked PyCharm Community Edition from Jet Brains. They are the guys who make ReSharper and IntelliJ, so they should know what they are doing.
This is the problem I have with new projects. I start work on them, and then all of a sudden I want to make some massive change, take a completely different direction, re-boot the whole thing. This has just happened to me with Rogue Code. I was making some coffee in the kitchen when I heard the sound of a police siren in the distance. Whenever this happens to me, I immediately flash back to various zombie films I have seen (i.e. suburban setting suddenly disrupted by cop sirens, gun shots, screams etc.). Anyway, while I was in this zombie frame of mind, I started thinking:
What if Rogue Code was about zombies instead of dungeons?
And then I was away, away in fantasy land. I was planning the URL I needed to buy (www.zombie-code.com is available), I was puzzling out the basic mechanics of the game etc. Note that I am doing this BEFORE I have written one line of code for the original dungeon-based concept. This is actually pretty typical of me. Sometimes I start a personal project, change the specification several times in quick succession, then move on to something else without achieving anything. I guess this is the challenge with personal projects, as opposed to the projects I am handed at work. When you are implementing your own specification, you are just as likely to encounter requirements drift as when you working on someone else’s. I am going go do some more work on the players guide, while I think about this.
OK, after a few false starts, I have created an image using Tiled to illustrate the type of thing I am talking about. It is very simple, but hopefully it paints a picture. A rogue enters a torch-lit dungeon. In the smoky light, he makes out a treasure chest and a dozing guard. Can he nab the treasure and slip away? Will the guard wake up or sleep right through? Just another day at the rogue office, right?
OK, so let’s get down to brass tasks. How is programmatic dungeon crawling going to work exactly? This is a good question. Well, I have started a new page called Players Guide that tries to answer it.
The best part of a project is always the beginning. The first few days or weeks of a new undertaking are full of possibility. Of course, it tends to bog down after that, but right now I am happy, because I have an idea. This is not a commercial idea. I am not trying to make money. It is not an original idea. I am sure that someone, somewhere has already done it. But it is a fun idea, or at least, it seems like fun to me.
What I want to do is take all the programming goodness you can find in Robocode, and throw it at the noble art of subterranean adventuring / tomb raiding. Think about it! You program the adventurer, then drop them into a procedurally generated, semi-random dungeon. You study the results. You improve the code. You change your strategies. Your adventurer evolves. Fun!
Anyway, it is all going to be very simple to begin with. In my mind I am seeing a small grid of tiles that represent a dungeon. Then we insert a sprite representing the rogue, a treasure chest, a few enemies and an exit. Add some very simple rules for turn based action and you are ready to go! It’s easier to explain this stuff using images, so my first research task involves finding a good map/dungeon editor. I have found a few links to something called Tiled, so that is where I am going to start.