Using Constants in C#

Programs often need to store data including numbers and other values that have specific meaning across the code. Constants enable us to store values that will be used in the program and give them easily understood context. Constants will be used heavily in our roguelike game and this chapter shows the first collection of constants that will be used in defining and constructing the map.

This post is part of a series on creating a roguelike game in C#. For the latest chapters and more information, please visit the Official Project Page on ComeauSoftware.com. The current code for this project is also available on Github.

Introduction

The MapLevel class that we added in the last chapter will contain all the code for actually generating the random map, including the hallways, within the 80 x 25 area assigned to it using specific ASCII characters. There are a lot of specific values used repeatedly during this operation like the maximum width of a room or the width and height of one of the regions containing the rooms.

I could just use these values within the code but it’s best to assign them names so they actually have some meaning to someone reading the code. This also prevents typos and one-off errors since the name assigned can be checked by the compiler to ensure it means something whereas a number would simply be accepted.

With a number that could change during the program, we would use a variable. These values don’t change so we declare them as constants which cannot be changed within the code.

This post is part of a series on creating a roguelike game in C#. For the latest chapters and more information, please visit the Official Project Page on ComeauSoftware.com. The current code for this project is also available on Github.

ASCII Characters

The first group that we’ll add are the characters that will be used to draw the rooms and hallways. Declaring these not only adds specific meaning to them in the code but makes it easier to write the code because Intellisense will supply the constant names later.

Immediately after the opening bracket for the MapLevel class that you added earlier, add the following lines. You can copy and paste the ASCII box drawing characters shown below into the code. They’re also available from https://www.w3.org/TR/xml-entity-names/025.html .

// Box drawing constants and other symbols.
private const char HORIZONTAL = '═';          
private const char VERTICAL = '║';
private const char CORNER_NW = '╔';
private const char CORNER_SE = '╝';
private const char CORNER_NE = '╗';
private const char CORNER_SW = '╚';
private const char ROOM_INT = '.';
private const char ROOM_DOOR = '╬';
private const char HALLWAY = '▓';
private const char STAIRWAY = '≣';
private const char EMPTY = ' ';
  • Notice that all of these constants are declared as private. There’s no need for any code outside of this class to reference them. Declaring them as private also keeps them from showing up in Intellisense when you’re working in another module so it cuts down on distractions.
  • After the const keyword, we specify the data type. C# requires us to specify what type of data is being stored whether it’s a whole number (integer), a text string or a True / False boolean value. The compiler will also hold you to the types you specify. This is what programmers mean when they say that C# is strongly typed. You cannot declare a constant or variable as a numeric value and then assign text to it. This helps prevent errors within the code.
  • The data type used here is the char type which can store a single Unicode character of information. Because Unicode characters have corresponding numeric values, this type supports some numeric conversion and comparison operations that a string with multiple characters wouldn’t which is one reason it’s included in the language. Trying to squeeze more than one character into a single char value would cause an error.
  • Notice that you use the single quotes (‘ ‘) to specify the character for a char value. This is required. Text strings use double quotes (” “).
  • Constant names are typed in ALL CAPS by convention although it’s not absolutely necessary. This makes them easily recognizable in code so it’s a good habit to keep.
  • Most of the box drawing symbols used here should be self-explanatory. ROOM_INT is a single period character used on the interior of rooms and the EMPTY character will be used wherever nothing else is shown, including outside of the rooms and hallways. For now, every one of the 2000 spaces within the 80 x 25 display will have a MapSpace object, even if it is EMPTY.

Room Dimensions

Next, let’s add some integer values for the map measurements.

private const int REGION_WD = 26;
private const int REGION_HT = 8;
private const int MAP_WD = 78;
private const int MAP_HT = 24;
private const int MAX_ROOM_WT = 24;
private const int MAX_ROOM_HT = 6;
private const int MIN_ROOM_WT = 4;
private const int MIN_ROOM_HT = 4;

These constants control the minimum and maximum dimensions for everything on the map. The useable map is 78 x 24 to keep the dimension divisible by 3 for a system of 3 x 3 room regions. The regions themselves are 26 x 8 and the rooms can be 4 x 4 to 24 x 6 in size. You’ll see all of these values used again and again in calculations.

At this point, it’s a good idea to start visualizing the map area itself. I originally tried to hold all this in my head while I was coding but it got too confusing when doing all the calculations so I made up a chart in Excel which you can find with the project code on Github.

Click for full-size image.

In the screenshot, you can see how the nine room regions will be laid out within the map space. The map spaces will be held within an array as you saw in the last chapter and array coordinates in C# are zero-based so the X-axis numbering runs from 0 to 79 and the Y-axis from 0 to 24. We’ll be leaving the top row of the display and one column on each side empty to achieve that 78 x 24 space I mentioned earlier.

A regular integer (int) value can have a maximum value of 2,147,483,647 and takes up 32 bits (four bytes) of memory. It’s the commonly used type for whole number.

Now, C# has other numeric types like the byte data type which can hold values from 0 (00000000 in binary) to 255 (11111111) and takes one byte of space in memory. The short type stores values from -32,768 to 32,767. It’s known as a 16-bit integer because it takes 16 bits (two bytes) to store the value. Since none of these values are going over 255, I asked the obvious question whether I should go with a byte data type and save the memory and storage space. With the incredible amounts of storage and memory in today’s machines, there’s not as much reason to worry about the space a single value takes in memory but it’s good to be aware of how much space your choices are using.

From my research online, it turns out that modern computer processors actually work with 32-bit integer values more efficiently so, unless I was working with a few million copies of the values, there is no practical benefit to using one of the smaller types. Still, I’m glad to have asked the question and you might run into similar questions as you continue coding.

Probabilities

Let’s add two more …

private const int ROOM_CREATE_PCT = 90;
private const int ROOM_EXIT_PCT = 90;

These constants will be used to determine whether individual rooms should be created during room generation and whether individual walls will receive an exit. Remember that the map rules allow for one or two rooms on each level to be missing and replaced with hallways and not every wall needs an exit so long as all rooms are connected. These constants will be used with the random number generator later on in order to make these decisions many times on each map. There will be a lot of these probability calculations within the game, especially as we get into fights between the player and monsters.

Your code should now look something like this:

You should be able to run the program without any errors by pressing F5. If you do get any errors, be sure to recheck your code against the code above and on Github.

Exploring Further

Before you move on to the next lesson, it would be a good idea to review the list of data types that I linked to earlier.

https://www.tutorialsteacher.com/csharp/csharp-data-types

It’s not necessary to memorize the types but look over them and notice how they’re used in some of the code samples on the page. If you have a separate sandbox application as I recommended in an earlier chapter, it would be a good idea to play around with declaring some of them in code and using them in basic operations to get familiar with them.

Next –