Thursday, September 12, 2013

Probabilities in Dominion (Part 1)


That's right, we're going to discuss the intersection of two sets: Mathematics and Incredibly Nerdy Card Games.  Dominion is a "deck-building" game that's all about making the best use of the (normally) very small amount of information you have.  Unlike a lot of common card games, each player has his own deck of cards for which he is (almost) completely in control of the cards going into his deck throughout the game. The trick is to composing your deck such that your chances of drawing useful hands frequently are high.  As I soon discovered after starting to determine what some of these probabilities come out to be, they get difficult to calculate.  Very quickly.  In fact, a large chunk of the research I've read on the topic (yes, I'm not the only dork interested in this topic.  See?) has been based on not calculating the exact probabilities, but approximating it using a couple thousand trials in a simulator (Geronimoo's  and Dominate seem to be the most popular).  While there is certainly a use for it, anyone who knows me will tell you that I'm something of a perfectionist, so approximations will not due, when an exact answer can be found!! None-the-less, it's a huge topic, so this will be the first of likely many posts about this topic.

In a brief introduction to the rules relevant for this post: you begin your turn by drawing 5 cards off the top of your "draw pile" (ie- your shuffled deck of cards you haven't used yet). [As a quick disclaimer so I don't piss off the experienced Dominion players: there's some nuances here. For instance, technically, your hand is drawn at the end of the previous turn, and there are circumstances in which you may have more or fewer than 5 cards, among others. However, for the purposes of our discussion, this generalized rule will do for now, as I'm only interested in discussing basic, initial hand probabilities at first.] Technicalities aside, you start your turn with the ability to use a single action (that is, play a card that says "Action" on it...), perform it's text, and make a single card purchase using $'s gained through action uses and the remaining "Treasure" (money) cards in your hand.  Generally, the "good" cards are more expensive, but it really depends on the "Kingdom" (the set of 10 differently-named cards available for purchase. This is usually chosen at random when setting up the game).  Finally, whenever you can't draw a new card when you're supposed to, shuffle your discard pile and it becomes your new draw pile.  So, it's very useful to know information such as "What is the probability that I draw enough money to purchase some card I'm interested in?"

Luckily, there has been plenty of research in probabilities throughout the centuries, in particular, solutions to the famous Urn Problem.  From this, Dominion probabilities can be modeled using a Hypergeometric Distribution, that is, any probabilistic distribution (ie- you drawing a hand of 5 Dominion cards) that has the following probability mass function (ie- the likelihood of you drawing exactly k cards of a particular desired type):

Where K is the number of cards of the desired type available in your draw pile, k is the exact number of the desired card you want in your hand, N is the total number of cards in your draw deck, and n is the hand size (5), and of course:

is the binomial coefficient, often read as "a choose b." It tells you how many possible ways exist for choosing b number of objects if you have a of of them to select from.

So, let's jump right in and put some numbers to it: let's consider the probability of drawing all 5 Coppers (Treasure cards you use as money, $1 each) on your first turn of the game! By the official rules, you start the game with 7 Coppers and 3 Estates (Victory point cards that don't do anything until the end of the game, at which point you use them to win), so K=7, k=5, N=10, and n=5. Because these calculations can be somewhat tedious when performing many times (and because I'm lazy), have some code:
(FYI- I used a more computationally efficient algorithm for the binomial coefficient, so don't freak out when you review the below code and realize it's not exactly what's defined above.  It's pretty basic combinatoric algebra, so I leave it as an exercise to the reader... I've always wanted to say that... though, you'll probably just look it up on Wikipedia, especially since I supplied the link above... *disapproving glare* ):

/// <summary>
/// Calculates the Binomial Coeffiencient, given two integers.
/// </summary>
/// <param name="n" >The count of the total drawing pool</param>
/// <param name="k" >The count of the subset you are selecting</param>
public static double CalculateBinomialCoefficient(int n, int k)
{
 double result = 1;

 for (int i = 1; i <= k; i++)
 {
  result *= (double)(n - k + i) / i; // must cast numerator as double to avoid integer division error
 }

 return result;
}

/// <summary>
/// Calculates the value of a probability mass function for a hypergeometirc distribution for a particular number of outcomes k
/// </summary>
/// <param name="N" >The count of the total population pool</param>
/// <param name="K" >The count of success states in the total population pool</param>
/// <param name="n" >The number of draws</param>
/// <param name="k" >The desired number of successes</param>
public static double CalculatePMF_HypergeometicricDistribution(int N, int K, int n, int k)
{
 return (CalculateBinomialCoefficient(K, k) * CalculateBinomialCoefficient(N - K, n - k)) / CalculateBinomialCoefficient(N, n);
}

Crank'n this out reveals the probability of drawing the awesome starting hand of 5 Coppers is 8.333 %.

Fun Fact: notice that the probability of drawing exactly 2 Coppers is exactly the same, just as drawing 3 or 4 Coppers on your first turn is 41.667 % in either case. Between your first and second turns, you use each of your 10 starting cards exactly once- there is zero randomness to your second hand. Since your first and second turns are played without any possible interaction between players (other than plotting and psychological triggering based on what other players bought...), the first and second hands, can be thought of interchangeably.  In some regards, it might be better to know that your probability of getting the 2/5 Copper split between your first and second hands is 8.333 + 8.333 =  16.667%, while getting the 3/4 split is 83.33%.

Knowing the probability of drawing EXACTLY a specific number of the desired card is useful, but you might also want the probability of drawing AT LEAST a specific number of the desired card.  Suppose you're set on buying a particular $3 card with your starting hand; what's the probability you can do it on the first turn? We'd need to find the probability of drawing exactly 3, 4, or 5 Coppers. So add 'em up! (again, I'm lazy, so have some code):

private const int HAND_SIZE = 5;
/// <summary>
/// Returns the probability of drawing AT LEAST the MinimumCount copies of the desired card
/// </summary>
/// <param name="CardType" >Name of the card to calculate the probability for</param>
/// <param name="MinimumCount" >Minimum number of cards of the given type desired</param>
/// <param name="Deck" >The current deck of cards</param>
public static double CalculateProbOfDrawingAtLeastXCards(string CardType, int MinimumCount, DominionDeck Deck)
{
 double probabilitySum = 0;
 for (int k = MinimumCount; k <= HAND_SIZE; k++)
 {
  probabilitySum += CalculatePMF_HypergeometicricDistribution(Deck.DeckSize, Deck.CardCount[CardType], HAND_SIZE, k);
 }
 return probabilitySum;
}

As you can see, I've added a DominionDeck class to the project, defined below... (perhaps I'll get it up to BitBucket or make a mobile version so you can run your statistics in the middle of your next game...)

public class DominionDeck
{
 public Dictionary<string int> CardCount { get; set; }
 public int DeckSize
 { 
  get
  {
   int total = 0;
   foreach (string entry in CardCount.Keys)
     total += CardCount[entry];
   return total;
  }
 }

 public DominionDeck()
 {
  CardCount = new Dictionary<string int>();
 }

 public void Initialize()
 {
  CardCount.Add("Copper", 7);
  CardCount.Add("Estate", 3);
 }

 public void AddCard(string CardName)
 {
  if (CardCount.ContainsKey(CardName))
   CardCount[CardName]++;
  else
   CardCount.Add(CardName, 1);
 }
}

Crunching these numbers gives you a 91.667 % probability of drawing AT LEAST 3 Coppers in your first hand.

Of course you aren't restricted to using these probability calculations on only the draw hand... you can run the same equations to figure out later in the game what your probability of drawing a hand full of Victory cards and Curses? (Curses are like Victory cards, only they are worth -1 point at the end), in other words, a useless hand. Well, if your first 2 purchases of the game were both Estates, you have a slim 0.126 % chance of drawing a useless hand on your third turn (but you can reconcile yourself knowing all that's left in your deck is Coppers, leaving you with a 100 % probability of drawing 5 Coppers your next turn).  Though, what's more likely to happen than drawing all Estates is drawing a hand with at least one Estate (AKA: a hand with at least one useless card, reducing your buying power to a maximum of $4); it's 97.348 % certain to happen on your third turn...

...Which brings me to the next topic I was going to mention: Deck Density. Buying up Victory cards is how you ultimately win the game, but they are (generally speaking) useless during the game, and act to fill up your hand with useless cards. As you saw with the last example, buying up victory cards early will very easily reduce the availability of useful cards like Actions and Treasures.  Suppose: instead of purchasing Estates with your first two hands, you purchase Silvers (you'll need the 3/4 Copper split discussed earlier to accomplish this, as Silver costs $3 to buy, but don't worry, you're 83.33% certain this will happen; Doctors perform procedures on worse chances than that! Statistics really puts things in perspective, doesn't it? I really hope you're still able to place your hopes and dreams on numbers after reading my blog... anyway, I'm getting off topic...).  You now have a deck with 9 Treasures and 3 Estates in it.  Hey, that's a 15.909 % chance of drawing a hand full of Treasure cards- almost double the chances of opening with 5 Coppers! Also, that's just your probability of drawing 5 Treasure cards; Silvers are worth more than Coppers when buying stuff! In case you're curious, this scenario is the beginnings of what's referred to as a pure "Big Money" strategy.  Honestly, there is enough number crunching in analyzing just your chances of drawing a certain amount of "purchasing power" (monies) to warrant it's own post (the likely focus of the future Part 2), so I'll hold off this discussion till another time, See the bullet points below for a preview.

So that's the basics (or at least what I'm comfortable publishing tonight).  If you want to think about the same things I am currently thinking about (probably a scary prospect, in general), consider:

  • There are cards such as Silver and Gold which are cards just like Copper, only worth $2 and $3, respectively.  What's the probability of drawing at least $X? we'll need to consider all the probabilities of drawing Treasure cards totaling (at least) X.
  • There are Action cards that can be played which cause you to immediately draw more cards.  What's the probability that you get such an action card and draw more of your desired card, up to your desired count, during these subsequent draws?
  • There are Action cards that can be played which cause you to be able to play more action cards. What's the probability that chaining the previous bullet point causes you to draw up to your desired count of desired cards?
  • There are Action cards that give you money to spend in addition to some action text like the previous two bullet points.
  • Whenever you can't draw anymore, you shuffle your discard pile, and draw from that newly created draw pile.  How does that change the probability, if you have to shuffle during any of the previous bullet points?
Just some tasty math noms for thought... 

Friday, August 9, 2013

Preparing for AI

Another programming excursion... can you tell I've been doing a lot of that lately? I've been reading up on AI programming  ("Artificial Intelligence", just in case anyone's not familiar with the abbreviation). Technically, it's "weak" AI I'm looking at now, but I'd love to include some strong AI elements in Spacer... Think about it: a game that actively learns how the gamer plays and adapts accordingly.  Sounds awesome to me

This is totally NOT what I'm creating... *shifty eyes*
But I digress,

For my purposes, preparing a game to handle an AI boils down to the old dilemma of whether to use inheritance or composition for game objects.  For now, the only objects in the game  that have any kind of self-control are Ships.  I'm not going into all the details on how the Ship class is setup, but I'd like them to be pooled objects.  Before I started messing with the bulk of the solution discussed below, the only ship present was an instance of a derived class; PlayerShip.  This worked fine for debugging up to this point, but here's why I no longer like using a derived class to exert control over an object:
  1. Using instances of both the base class and its derived class (or instances of two different derived classes) screws up the existing object pooling.  If you have 2 ships in your pool, the first is a PlayerShip and the second is an BotShip, you're gonna need to either re-instantiate when you need a new object (which defeats the whole purpose of object pooling), do a lot of fancy casting to make it work (which is just a headache), or re-design ObjectPools entirely to store and fetch multiple types of objects within the same pool (which, let's face it, is too much work)
  2. The game shouldn't care whether it's updating a PlayerShip or a BotShip!  You don't want a human player or an AI player treated any different when calling the Update method, for instance.  If you do, it's very easy to give abilities to one or the other, or force yourself to repeat logic to prevent that from happening.  If a ship needs to move 200 meters starboard, it doesn't matter who issued the order (Captain Picard or the Borg queen), the game logic should move the ship the same way.
So, how to we address the issue instead? Ask yourself this: what do both ships have or own (as opposed to are or are not)? They both have a Controlling Entity! Not to get too philosophical, but here "Entity" is taken to mean "An independent, decision-making thing". (super technical. I know, right?). But both a human player or an AI decision tree meet this definition! The Entity class just contains a set of basic properties that the ship looks at during it's update phase:

abstract class Entity
{
 // Ship's Input variables:
 protected Vector2 desiredFocalDirection; // The direction the pilot wants the ship to face
 protected Vector2 desiredEngineFireDirection; // The direction the helmsman wants the ship to move
 protected bool activatePrimaryEquipment; // Should you attempt to activate any onboard equipment?
 // Other properties omitted... you get the idea.

 #region Public Properties

 public Vector2 DesiredFocalDirection { get { return desiredFocalDirection; } }
 public Vector2 DesiredEngineFireDirection { get { return desiredEngineFireDirection; } }
 public bool ActivatePrimaryEquipment { get { return activatePrimaryEquipment; } }

 #endregion

 public Entity(){ }

 public abstract void LoadContent(ContentManager content); // Load any resources derived entities need (ex: screen output information for the player to make decisions, or an AI personality)
 public abstract void UnloadContent(); // Clean up!
 public abstract void Update(GameTime gameTime) // Update your ShipInputVariables!

 public virtual void Initialize(Ship givenShip)
 {
  shipControlled = givenShip;
  shipControlled.SetControllingEntity(this);
 }
}

These Entities are maintained by something outside the physics or gameplay calculations, and the "Ship's Input Variables" are set in derived classes: PlayerEntity and BotEntity. In PlayerEntity, they are set by the controller/keyboard input, and in BotEntity: by the AI logic.  Now, we just have to give each ship an Entity (via a SetControllingEntity method), from which it reads it's input variables during it's Update phase. To help clarify, have a much-simplified UML diagram:



So that's it, composition wins out over inheritance... this time.  What's even better? We have a clean separation of responsibility! LocalSpace handles all the physics engine calculations and collision detection, the PlayerEntity handles translating input device states into intention, and the BotEntity handles translating AI decision-making into intention.  Heck, throw an EntityManagement class in there storing a collection (or ObjectPool, anyone?) of Entities to manage adding/removing players: when a player leaves, swap his ship's ControllingEntity with a BotEntity (that's been learning his gameplay style... again, I'm not creating SkyNet), and the action can continue when your friend gets called home for dinner! Feeling like making things more awesome? Create a new class derived from Entity: SquadronEntity, that contains a list of BotEntities, and can override decisions made by the individual bots for a coordinated battle strategy!

... this is how the Technological Singularity will come about, isn't it?...


Thursday, August 1, 2013

Pythagoras and Legos

I think it's safe to say that when we've all started out building with Lego bricks, we've all stuck to the rectilinear grid that's sooo~ conveniently provided by the baseplate (or you had a really dull childhood and didn't build with legos).  Even some of us adult fans of lego are guilty of "sticking to the grid," but today is the day to "Think outside of the block!" (Lego pun #1)

When you sit down for some (as Sheldon puts it) Lego Fun Time, think of each block as it's own right-angled coordinate system of integer-spaced connection points.  Thinking this way, you remember from your really cool high school geometry class that there was this old "stud" (pun #2) named Pythagoras who had quite a bit to say about integers and right angles. It's all conveniently summed up as Pythagorean Triples; that is, sets of integers (a,b,c) that satisfy the equation below.  For your reference, a and b (the two smaller sides that are at a right angle to each other) are called "legs" and c is the "hypotenuse"


Most people are probably thinking "oh man, it's getting mathy, that's about as boring as sorting pieces!" Well, don't be a blockhead (pun #3).  It's easy math, and I'm not asking you to solve equations; you can just look up lists of available Pythagorean triples and use whatever one is convenient for what you're building.  So, what does an old Greek dude have to do with little plastic bricks? Well, Legos only come in integer-sizes, unless, of course, you've cut your pieces >:( , Pythagorean triples are a perfect way to build non-rectilinearly!

There are two ways to use these magic triples: creating "Interior" or "Perimeter" right triangles.  I'll start with Interior, since you can roll with an unmodified list of Pythagorean triples.  For these, you'll need at least two "hinge" pieces to form the angles (this or this; there's probably a couple other pieces that'll work, but these are the most obvious to work with).  The whole idea about using Interior triangles is that the Pythagorean triangle is actually the negative space formed by enclosing a region:

In this example, we've used the (3,4,5) triple.  Your enclosing lengths are equal to the leg and hypotenuse values of your triple, counting in "stud units".  Know what'll make you put on a yellow smiley face? (#4ish) Knowing these triangles can scale! You can double all the lengths of any "primitive triple" (Pythagorean triples where each value is coprime to the others), and it'll still work; (6,8,10), for instance.  

As for Perimeter triangles, you don't need any special pieces, just something to bump your hypotenuse above the studs of your baseplate (I used these for the pic below, but really almost an piece will do).  For these, the bricks themselves form the perimeter of the right triangle.  Now, something REALLY important is our length counting system! Normally, when discussing lego piece sizes, we describe sizes something like "2x4", meaning "two studs by four studs", but the values in the Pythagorean triangles are LENGTH measurements, not UNIT measurements, so for the lego versions of the perimeter triangle to work, we need to measure the distance between studs, not the stud count! This example sums it up:
This example uses the (3,4,5) triangle again, but the lengths, in terms of stud count, is (4, 5, 6).  So, the Pythagorean triples you use when constructing must modified by adding 1 to each value to get to the "standard" Lego stud sizes:


Again, this works for any set (a,b,c) of Pythagorean triples, and can be scaled as well.  

So, those are the basic two uses of Pythagorean triples in Lego building.  

But let's not stop there, our brick bucket isn't empty, yet! (I don't think that counts as a pun...) Strictly speaking, Legos aren't restricted to integer values; with "jumper" plates (this and this, for example), we can make use of the half-integer values, too! These are a little harder to work with, especially since all of the commonly used1 primitive Pythagorean triples have an odd hypotenuse length.  But, it opens up quite a few possibilities. You can take half of each value of a Pythagorean triple (for interior triangles) or a modified triple (for perimeter triangles) for new stud unit sizes.  Below are half-sized examples of the previous two (3,4,5) triangles: 
Half-sized Interior
Half-sized Perimeter

(sorry, camera position parallax distorts them a bit, but they do line up). For clarity, there are two Jumpers in each photo, one in the hypotenuse and another in the vertically-oriented arm.  

So, theory is great, but how do they "stack up" in practice? You tell me! (that totally counts, btw)
This is the apse of my cathedral (Yes, these photos are shameless self-promotion :P ).  It uses two side-by-side (3,4,5) triangles, that might be better seen in the interior shot, where you have the floor tiling to use as guides for where the hinge connections are made.  Also note that you don't have to form a "triangle" using the interior triangle method, you can use the triangle concept to guide where your hinges need to be anchored to the baseplate.

I put together this is a quick 3-minute MOC just for you all, showing a farmer's beat-up, old cow pasture. It shows the use of two perimeter-method triangles; the left-most fence is the (5,12,13) triple, so of course it has a length of 14 studs. That's the easy (and less interesting) one.  The middle patch of fence shows the halving method on a (5,12,13) triple, so it's length is 7.5 studs, and if you count out the stud placement on the ground it comes out to 3.5x7.  Or the arithmetic, if you prefer :

(we're subtracting 1 rather than adding because we're converting from stud units to length units to use in the Pythagorean equation (the first equation in this post), whereas before, we were doing the reciprocal conversion)

So, much better than always working with square, hard edges! In my opinion, at least...

For your convenience, there are eight triples in particular (and their multiples!) that seem to work very well:  
  • (3,4,5)
  • (5,12,13)
  • (8,15,17)
  • (7,24,25)
  • (20,21,29)
  • (12,35,37)
  • (9,40,41)
  • (28,45,53)
I cut the list off at these eight simply because anything with larger arms begins to exceed the standard 48x48 stud baseplate size.  But don't let me stop you if you want to attach several plates together!

I'll take a moment to mention a deviation from this "pure" geometry: like all manufactured plastic parts, there are some mold tolerances engineered into the bricks, which means you can "fudge" the numbers a bit.  For instance, (19,11,22) and (6,15,16) are both approximate Pythagorean triples, which work within the "give" of the lego parts.  These little number fudges don't make a whole lot of difference on small scales, but they can be a source of serious tension when they add together, and can really affect the structural integrity of your model.

So, go sit down the best you can with single-jointed, plastic legs, and have a geometric time!  Happy Building!


1 That'd be an interesting proof: do all primitive Pythagorean triples have an odd-valued hypotenuse? I'll give it some thought after I finish this post.

EDIT: Turns out, yes, the hypotenuse MUST be odd for any primitive Pythagorean triple:
According to this paper, theorem 1.2 shows the hypotenuse can be written:


WLOG, let m be odd (m = 2+ 1), and n be even (n = 2l), where k and l are both integers and selected such that m and n are coprime (though, I state this for completeness, the fact they are coprime is inconsequential to our result).  After some algebra, we get:


Which, of course must be odd, for any pair k and l.  QED.

Wednesday, July 31, 2013

Object Pooling: Resource Recycling!

Alright, so we're 2-for-2 regarding posts about Spacer; I guess I've made quite a lot of progress on it lately. The concept of object pooling isn't new to programming, but it's new to me.  As an added bonus, this concept is very useful to programming things other than just video games; I could easily see it being used for particle physics or computational chemistry simulations (or, I suppose hundreds of business-type applications).  Therefore, that will be the topic of this post.  Object pooling is essentially digital recycling.

I guess Randall Munroe's stick figure comics are influencing me.  Also the fact that this comment is in the alt text.


The basic idea is to avoid constantly creating and destroying objects, which is very processor-expensive when you're dealing with potentially hundreds of complex objects. To accomplish this, we create a bunch of "empty" instances of the class we need, activate them as needed, and deactivate them for reuse when we're done. That is, instead of "new-ing up" a bullet every time an in-game gun fires, turn to your "bullet pool" and activate the next inactive bullet.

There are a couple keys to making object pooling work:

  1. All objects being pooled should have an empty constructor
  2. The pooled objects should all have some "initialize" or "activate" method
  3. Be sure to clean up the object after you are done using it... you don't want any residual properties left over from the previous usage. 
Without further adieu, here's the code sample for my generic pooling:
class ObjectPool<t> where T : IPoolableObject
{
 #region Private Fields

 private List<t> activeItems;
 private Queue<t> inactiveItems;
 private int resizeAmount;

 #endregion

 #region Public Properties

 public T this[int index] { get { return this.activeItems[index]; } }
 public int Count { get { return activeItems.Count; } }
 public List<t> InactiveObjects { get { return inactiveItems.ToList(); } }

 #endregion

 public ObjectPool(int Capacity, int ResizeAmount = 1)
 {
  if (Capacity < 1)
   throw new ArgumentOutOfRangeException("The capacity must be 1 or greater");
  if (ResizeAmount < 1)
   throw new ArgumentOutOfRangeException("The resize amount must be 1 or greater");

  activeItems = new List<t>();
  inactiveItems = new Queue<t>();
  resizeAmount = ResizeAmount;

  //Fill up the inactive items list
  System.Reflection.ConstructorInfo constructor = typeof(T).GetConstructor(new Type[0]);
  for (int i = 0; i < Capacity; i++)
  {
   inactiveItems.Enqueue((T)constructor.Invoke((object[])null));
  }
 }

 public T ActivateItem(string ObjectModelName, Vector2 StartingPosition, float StartingOrientation, Vector2? StartingVelocity = null, float StartingAngularVelocity = 0)
 {
  if (inactiveItems.Count == 0) //enlarge the queue if we've run out of objects! 
  {
   System.Reflection.ConstructorInfo constructor = typeof(T).GetConstructor(new Type[0]);
   for (int i = 0; i < resizeAmount; i++)
   {
    inactiveItems.Enqueue((T)constructor.Invoke((object[])null));
   }
  }

  T newItem = inactiveItems.Dequeue();
  activeItems.Add(newItem);
  newItem.ActivateObject(ObjectModelName, StartingPosition, StartingOrientation, StartingVelocity, StartingAngularVelocity);
  return newItem;
 }

 public void DeactivateItem(T item)
 {
item.DeactivateObject();
inactiveItems.Enqueue(item);
  activeItems.Remove(item);
 }

 // The stuff below just makes the ObjectPool fancy, allowing you to call a "foreach" loop on the pool or simply get a list of all the active objects:
 public IEnumerator<t> GetEnumerator()
 {
  return (IEnumerator<t>)this.activeItems.GetEnumerator();
 }

 public List<t> ToList()
 {
  return activeItems;
 }
}

IPoolableObject, here is a really simple interface inherited by your pooled objects. It's everything that's needed for a "new" bullet and everything it needs to clean up after itself (dirty object..).  The ObjectModelName is used as a lookup value for both a pre-loaded texture and the statistics library entry (ex: health, speed, armor, ect..., remember? See the previous post if you don't). StartingPosition, StartingOrientation, StartingVelocity, and StartingAngularVelocity are all relavent quantities for the physics engine.  As for cleanup, everything of relevance gets overwritten during the ActivateObject call, so there really isn't anything to the DeactivateObject method.  Your objects don't have to have these parameters (or lack-there-of), I'm just throwing them out there as an example.

interface IPoolableObject
{
 void ActivateObject(string ObjectModelName, Vector2 StartingPosition, float StartingOrientation, Vector2? StartingVelocity = null, float StartingAngularVelocity = 0);
void DeactivateObject();
}

Also, the bit about the "ResizeAmount" isn't really necessary. I just wanted something to fall back on if the initial capacity guess was too small and isn't the app barfing by throwing a "queue empty" exception.  You could just have the pool return null; make sure your code knows how to interpret a null object, though.

As for how it's implemented in the larger game, it's super effective!

// Create and construct the pool:
public ObjectPool<bullet> BulletPool;
BulletPool = new ObjectPool<bullet>(100);

// Activate a new bullet:
Bullet newBullet = bulletPool.ActivateItem(BulletTypeFired, StartingLocation, StartingOrientation, StartingVelocity);

// Insert a bunch of code about how your app uses the newBullet

// and finally, deactivate it when you are finished:
BulletPool.DeactivateItem(newBullet);

So there you have it.  I'd like to give some credit to a couple other sources that I kinda picked and chose my favorite parts from: Jason Mitchell, Thomas Aylesworth, and the XNA Wiki: Generic Resource Pool.

Thursday, July 18, 2013

Keeping track of object stats: A database-like layer to video games

First post, woo-hoo!
I love video games; so much so that I'm going to start things off with a video game I've been working on (and is still very much in development).  It's a top-down 2D RPG/Adventure where the player pilots a customized ship, written in C#/XNA, and this will probably not be the last time you read about "Spacer" on this blog...  The quandary conquered stems from setting up something that's considered good programming practice: layers. Maybe it's already a known solution, but I couldn't find much when I was researching it...

I don't really see much of this in scientific applications, but in a lot of business-type applications, there are often three layers: database, logic, and view.  I'm going to focus on the separation of the database and logic layers.

In my initial commit to the game, every object had it's own hard-coded statistics associated to it.  For instance, the WeaponSystem class looked something like this:

class WeaponSystem : Equipment {
bool canFire;
double rateOfFire;
float energyPerShot;
int numOfShots, numOfShotsMax;
TimeSpan timer;

public WeaponSystem() {
//arbitrary values:
rateOfFire = 0.25; //in seconds
energyPerShot = 10f;
energyReserve = 100f;
numOfShotsMax = 50;
equipmentName = "Weapons";

//calculated quantities:
energyReserveMax = numOfShotsMax * energyPerShot;
numOfShots = (int)(energyReserve / energyPerShot);
timer = TimeSpan.Zero;
}

//There is more code about how the WeaponSystem works, but not important here
}

(hey, don't judge. Guns are important to a space adventurer... Jayne agrees, see?)

This would mean creating a class for every type of weapon system: lasers, missiles, torpedoes, etc... Bleck! The logic of the game shouldn't depend on the specific values you give to your objects, and honestly, shouldn't care what the values are so long as they are valid values!

Since hard-coding values is bad, the idea was to create a sort of "Stats Library;" a single place to store all these values to look up as needed. This really just meant separating the "arbitrary values" in the above code to it's own serializable class, and dumping it's corresponding XML file(s) into the content project. For organization's sake, all these "StatsLibrary objects"  were put into a separate VS project (this project comprises the data layer, the original game project is the logic layer).  The separation turned out something like this:
public class WeaponSystemStats
{
public string Name { get; set; }
public string BulletTypeFired { get; set; }
public double CoolDownTime { get; set; }
public float EnergyPerShot { get; set; }
int MaximumNumberOfShotsStored { get; set; }
}

<?xml version="1.0" encoding="utf-8" ?>
<XnaContent>
<Asset Type="Spacer.StatsLibrary.WeaponSystemStats">
<Name>TestGun</Name>
<BulletTypeFired>TestLaser</BulletTypeFired>
<CoolDownTime>0.25</CoolDownTime>
<EnergyPerShot>10.0</EnergyPerShot>
<MaximumNumberOfShotsStored>50</MaximumNumberOfShotsStored>
</Asset>
</XnaContent>

(keep in mind, this advancement didn't happen over sequential commits, the WeaponSystem class evolved a bit.  For instance "RateOfFire" turned into "CoolDownTime", also note the "BulletTypeFired" string: this same idea was applied to the projectiles fired from the WeaponSystem)

Now, we have a nice way to store data, a "database layer," if you will. Rather than each object storing it's own statistics, all it has to know is what kind of object it is (use a unique string, enum, or whatever other identifying method you want; in this example, it happens to be the "Name" property), everything else can just be looked up from a single source! Originally, this single-source was just a Dictionary object, which works fine for getting things to work, but why stop there?

Being completely OCD, the content project was already organized into folders of XML documents for each game object, why not load everything at once? Create a generic StatisticsLibrary object:
class StatisticLibrary<t> where T : IStatsLibraryEntry //This is just an interface with a single property: Name
{
Dictionary<string, T> library;

public void LoadLibrary(ContentManager Content, string contentFolder)
{
library = new Dictionary<string, T>();

DirectoryInfo di = new DirectoryInfo(Content.RootDirectory + "\\" + contentFolder);
if (!di.Exists)
throw new DirectoryNotFoundException();

FileInfo[] files = di.GetFiles("*.*");
foreach (FileInfo file in files)
{
string extensionlessFile = Path.GetFileNameWithoutExtension(file.Name);
T value = Content.Load<t>(contentFolder + "\\" + extensionlessFile);
string key = value.Name;

library.Add(key, value);
}
}

public T GetStatistic(string Name)
{
return library[Name];
}
}

Now, we can use a static object of each game object to lookup whatever data we need, where ever we need it! Just use the static object to access it from the logic layer. (Theoretically, you can access our new statistics library from the view layer, but it's generally regarded as bad coding practice; only allow a layer to access an adjacent layer). Now, you don't have to load everything in the specified content sub-folder, I just do because Spacer is still in pretty early development stages, and I don't have enough content to notice a difference in performance, but if you've got a lot of complex content, selectively loading material would be worth considering.

So there you have a basic database layer for a game:

  •  Create a serializable model for each game object (you can use inheritance to greatly simplify these!) that is separate from your logic that updates the object
  • Make as many XML (or JSON, or binary,...) docs as you want for object data
  • Populate and access via a static library object