Helpers and Extension Methods for Games

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:

        public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> 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.

        /// <summary>
        /// Returns a boolean denoting the result of a 1 in chance dice roll
        /// </summary>
        /// <param name="chance"></param>
        /// <returns></returns>
        public static bool Roll(int chance)
        {
            Random rnd = new Random();

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

I use this everywhere in my games:

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.

        /// <summary>
        /// Select a random number
        /// </summary>
        /// <param name="max"></param>
        /// <returns></returns>
        public static int PickRandomNumber(int max)
        {
            if (max <= 0) return 0;

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

    }
&#91;/sourcecode&#93;

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

&#91;sourcecode langiage="csharp"&#93;
Character.Health = Dice.RickRandomNumber(10); // allocate character's initial health
&#91;/sourcecode&#93;


<strong>Extension Methods</strong>

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


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

            return enumeration.ElementAt(idx);
        }

        public static T SelectRandomElement<T>(this IEnumerable<T> enumeration, Func<T, bool> predicate)
        {
            var filteredList = new List<T>();
            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); [/sourcecode] 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.

One thought on “Helpers and Extension Methods for Games

  1. Pingback: Unit Testing Methods With Random Elements (in MVVM Cross) | The Long Walk

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.