ASP.NET Website #3: Maze

This is the third in my series of creating ASP.NET web applications in order to learn more about this exciting technology :-)

It’s been a little while coming but that’s because after nearly finishing it, I had an idea of how to extend it further (by detecting if more people are using it) and I became excited about my new Pluralsight membership! This one is a little different in that it is inspired by the idea of a maze (or I suppose a kind of adventure game), where you can go north, south, east and west. If you remember games like this, you will know what I mean. They usually had text only and could be quite fun. In fact, my first memories of a game for the Commodore 64 was just such a game that I spent hours playing until I solved it.

The Screenshot

To put things into context, here’s a picture of what it (mostly) looks like. I took this image using the excellent Ember, which is a tool for taking screenshots and was created by ByteBlast. You can find it on Github as Open Source but there’s also a blog which he has created and is worth taking a look at.

How to Play

I know I am stretching the meaning of play…bear with me! As you can see in the image above, there is a big white box. That’s where I show a representation of the map, but rather than make it all visible, I hid it. What I had in mind when doing this was those 2d, top-down games where you are in a dungeon and can only see a few squares around you.

To make the area visible, just move the mouse over the squares inside and you can see how the terrain looks. Brick walls can’t be passed through and those brown squares are walkable. Using the four direction buttons means you can go in that direction, but only if the button is enabled.

To make it easier, you don’t have any of those situations where you type “go north” and it responds: “You can’t go that way.” The aim of the game is to make it to the destination, which will be told to you, once you arrive.

One other feature is if someone else is playing, it also keeps track of where they are and if you find yourself in a location with at least one other person, it will tell you that you are not alone. I guess if it doesn’t say that, you are alone, which is a depressing thought!

Representing the Map

The map is a simple array of Location, represented like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Location(int num, String desc, int north, int east, int south, int west)
{
if (desc == null)
{
throw new ArgumentNullException("Must provide a valid location description.");
}

LocationNumber = num;
Description = desc;
LocationNorth = north;
LocationEast = east;
LocationSouth = south;
LocationWest = west;
}

I use this in my LocationMap class statically. I did think about creating a text file and importing that, but this is only a simple application and I can’t see myself creating any other mazes.

1
2
3
4
5
6
gameMap = new Location[] {
//
new Location(0, "You are in a dingy hole. There is a strange smell.",
-1, -1, 1, -1),
new Location(1, "You are standing in a large cave. You can see light to the south.",
0, -1, 2, -1),

The first integer represents that location number you can see in Location. Any -1s are to indicate a dead end. All quite straight forward.

Using jQuery

This is the part that got me quite excited! To begin, I created a DIV which contained all my images as you can see below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div class="maze">
<!\-\- 5x6 grid -->
<img src="Images/wall.png" /><img src="Images/ground.png" /><img
src="Images/wall.png"
/><img src="Images/wall.png" /><img src="Images/wall.png" /><br />
<img src="Images/wall.png" /><img src="Images/ground.png" /><img
src="Images/wall.png"
/><img src="Images/wall.png" /><img src="Images/wall.png" /><br />
<img src="Images/ground.png" /><img src="Images/ground.png" /><img
src="Images/wall.png"
/><img src="Images/ground.png" /><img src="Images/wall.png" /><br />
<img src="Images/wall.png" /><img src="Images/ground.png" /><img
src="Images/ground.png"
/><img src="Images/ground.png" /><img src="Images/wall.png" /><br />
<img src="Images/wall.png" /><img src="Images/wall.png" /><img
src="Images/wall.png"
/><img src="Images/ground.png" /><img src="Images/wall.png" /><br />
<img src="Images/wall.png" /><img src="Images/wall.png" /><img
src="Images/ground.png"
/><img src="Images/ground.png" /><img src="Images/ground.png" /><br />
</div>

The original plan was to detect when the mouse hovered over an image and then remove it. I thought initially that that would just hide it, but it really does remove it which isn’t what I had in mind! The first time I tried that, it was as if I had dropped a nuclear bomb on my map - there was literally nothing left! My way to achieve the effect was to play with the opacity. If the mouse is on one of the tile images, I set the opacity to 1, which means it is completely visible. Otherwise, it returns to 0, hiding it. Here is the incredibly small amount of jQuery needed to do that (yes, I love it!):

1
2
3
4
5
6
7
8
9
10
$(document).ready(function () {
$(".maze > img").mouseover(function () {
var objimg = event.target;
$(objimg).css("opacity", "1.0");
});
$(".maze > img").mouseleave(function () {
var objimg = event.target;
$(objimg).css("opacity", "0.0");
});
});

Briefly, it waits until the page has loaded (line 1), then for all of the image elements that are within the “maze” class (line 2), I set an event handler for when the mouse goes over them (end of line 2). Next, when the event fires, I grab a pointer to that image, and then change it’s opacity (line 4). I rinse and repeat this (but using a different opacity) for the mouseleave event.

Detecting Other Players

I’ve already been using ViewState quite a lot to track information between page loads but this time, I needed something a bit further reaching so that I could track usage across all uses of the app. Tada…enter the Application state! I use this to record how many people are in each location so that I know whether to tell the player that they are not alone. There were two things that I needed to think about when using this:

  1. How to make sure that I didn’t have any concurrency problems where two users would try to access the state at the same time and,
  2. How to represent the number of players in a location.

Let’s start with the representation. I decided to store this simply as an array of integers which had a size that matched the size of the map.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// <summary>
/// Creating a set of locations of size "locations".
/// </summary>
/// <param name="locations"></param>
public PeopleWatcher(int locations)
{
if (locations < 1)
{
throw new ArgumentException("Argument must be 1 or greater.");
}

Locations = locations;
peopleInLocation = new int\[locations\];
}

Then, I needed to know about the comings and goings, as they say, between locations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// <summary>
/// Moving from one location to another within the map.
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
public void GoToLocationFromAnother(int from, int to)
{
if (Math.Min(from, to) < 0 || Math.Max(from, to) > Locations - 1)
{
throw new ArgumentOutOfRangeException("Location must be between 0 and " + (Locations - 1));
}

if (from != to)
{
peopleInLocation\[from\] -= 1;
peopleInLocation\[to\] += 1;
}
}

Provided I knew where someone was and where they were going, that would be fine. You can see that I made use of the Math static methods for min and max to cheat when detecting any invalid input. To me, that looks a little cleaner but I’d like to know of any better way. Perhaps the long form of: if (from < 0 || to < 0… **is **better because less thought is needed to know what I am doing? I don’t know. It reminds me of a Pluralsight video with Jon Skeet where, when talking about regular expressions, he advocates just keeping things simple so that you don’t have to think. Lastly, to cater for the start state where you begin the game, I allow someone to ‘appear’.

1
2
3
4
5
6
7
8
9
10
11
12
13
/// <summary>
/// Used when a new entrant joins the map to register their presence.
/// </summary>
/// <param name="loc"></param>
public void AppearAtLocation(int loc)
{
if (loc < 0 || loc >= Locations)
{
throw new ArgumentOutOfRangeException("Location must be between 0 and " + (Locations - 1));
}

peopleInLocation\[loc\] += 1;
}

What about that concurrency issue? That was easier than I thought. All you need to do is to wrap your access to the Application state within a lock and unlock, like this:

1
2
3
Application.Lock();
Application\["PeopleWatcher"\] = pw;
Application.UnLock();

How Could I Improve It?

I do wonder whether the way I designed this is as optimal or elegant as it could be. For example, I am not entirely sure that I should have stored the player’s location within the map object - I didn’t discuss that aspect above, so you’d need to look at the code to see. I also would have liked to indicate where a person was on the map - that would have made it easier to see where you are. Not necessary but as I first started using this, I would sometimes get completely lost! Perhaps that adds to it, I don’t know. Lastly, I keep thinking about that concurrency. It is not usually that easy, which makes me think I have missed something really obvious _and _which would break it. When the number of users of my apps reaches the lofty heights of 2, I will keep a closer eye on it.

Lessons Learned

  • jQuery is amazing! Therefore, by extension, so is Javascript, I guess.
  • I usually create projects and when I am done with them (bar any further mistakes), I post them on GitHub. For the next project, I will use GitHub properly, like a proper source control system, and update that, daily. That will certainly help with my git command line practise.
  • If you inspect an element (in Chrome, for example), that shows you how things are. If you view the source, it shows you what they were when you first created the page.
  • The use of Application state and locking/unlocking it.
  • Greater understanding of NUnit - I used that again, this project. But. And it’s a big but. I am not overly happy with how I tested the map location connections because they are baked into the class. I think it would have been nicer if I could dynamically alter the map from outside the class and then, make more flexible tests. I change the map, I might break the tests. That’s not so good!

Here you can find the Maze website application with the source in the GitHub repository.


Hi! Did you find this useful or interesting? I have an email list coming soon, but in the meantime, if you ready anything you fancy chatting about, I would love to hear from you. You can contact me here or at stephen ‘at’ logicalmoon.com