Helpers and Extension Methods for Games

July 20, 2014

Typically, extension methods and helpers are small methods that allow the same functional code to be re-used. An excellent (and useful) example is this:

[sourcecode langiage=“csharp”] public static void ForEach(this IEnumerable enumeration, Action action) { foreach (T item in enumeration) action(item); }




The above extension method provides a \`ForEach\` method for \`IEnumerable\`.

**Are games different?**

Yes.

Because a method for a game may involve a random algorithm.    Below are a collection of methods that I've found useful.

**Helper Methods**

I've put all the following methods in a class called Dice.  Obviously, for the first method, the name only makes sense in the context of a Dice class.  

[sourcecode langiage="csharp"]
        /// /// Returns a boolean denoting the result of a 1 in chance dice roll
        /// 
        /// 
        /// 
        public static bool Roll(int chance)
        {
            Random rnd = new Random();

            return rnd.Next(chance) == 1;
        }

I use this everywhere in my games: [sourcecode langiage=“csharp”] If (Dice.Roll(10)) // character has a 1 in 10 chance of dying if they drink the potion




The next method is basically just a wrapper for Random.Next.  

Tip: You may find the random algorithm works best if you maintain the \`rnd\` variable for the life of the game.  The reason for this being the the default random seed is taken from the system clock.  Consequently, calling the same method in rapid succession may result in the same number being returned.

[sourcecode langiage="csharp"]
        /// /// Select a random number
        /// 
        /// 
        /// 
        public static int PickRandomNumber(int max)
        {
            if (max <= 0) return 0;

            Random rnd = new Random();
            return rnd.Next(max);
        }

    }

The context for this would be to allocate a number, for example:

[sourcecode langiage=“csharp”] Character.Health = Dice.RickRandomNumber(10); // allocate character’s initial health




**Extension Methods**

The following methods allow you to return a random element from a collection; the second allows for a predicate before the element is selected.

``` csharp

    static class EnumerableExtensions
    {
        public static T SelectRandomElement(this IEnumerable enumeration)
        {
            Random rnd = new Random();
            int idx = rnd.Next(enumeration.Count());

            return enumeration.ElementAt(idx);
        }

        public static T SelectRandomElement(this IEnumerable enumeration, Func predicate)
        {
            var filteredList = new List();
            enumeration.ForEach((item) => { if (predicate(item)) filteredList.Add(item); });

            Random rnd = new Random();
            int idx = rnd.Next(filteredList.Count());

            return filteredList.ElementAt(idx);
        }
    }

A typical usage for these would be:


var enemy = AllCharacters.SelectRandomElement(e => e.CharacterType == CharacterTypes.Enemy);

Unit Tests

The biggest problem with code like this, is that it’s difficult to unit test. For example:


        [TestMethod]
        public void TestSelectRandomElementPredicate()
        {
            List testCollection = new List();

            testCollection.Add(1);
            testCollection.Add(3);
            testCollection.Add(5);
            testCollection.Add(7);
            testCollection.Add(9);

            int e = testCollection.SelectRandomElement(n => n < 2);
            Assert.AreNotEqual(e, 1);  

            e = testCollection.SelectRandomElement(n => n < 2);
            Assert.AreNotEqual(e, 1);  


Certainly, if the method is bug free, then this will always pass; however, there may be a bug that only ever occurs when the first or last element is chosen.

In the next post I’ll cover how to unit test methods with random elements.



Profile picture

A blog about one man's journey through code… and some pictures of the Peak District
Twitter

© Paul Michaels 2024