<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://weblogs.asp.net/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Justin Rogers : Terrarium, Algorithms</title><link>http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/Algorithms/default.aspx</link><description>Tags: Terrarium, Algorithms</description><dc:language>en</dc:language><generator>CommunityServer 2007 SP1 (Build: 20510.895)</generator><item><title>Game Development: Book resources for users interested in more in-depth world generation discussion.</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/10/13/241646.aspx</link><pubDate>Wed, 13 Oct 2004 09:57:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:241646</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=241646</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/10/13/241646.aspx#comments</comments><description>&lt;p&gt;I think every book kind of covers world generation in an off-handed manner. There are very few books actually focused on the process of world generation alone. By world generation, I don't just mean the creation of terrain, and I'm instead talking about the population of the final world space with objects that the user can interact with. Terrain is sometimes part of the process (Tribes), but only comprises a portion of world generation.&lt;/p&gt; &lt;p&gt;Normally this manifests in games where space is of the primary concern. The layout of space is fixed (aka empty and unimportant) while the units that define a location in space are important, the actual space isn't really all that important. World generation comes into play with the dynamic and/or static creation of planets, trade routes, goods and services, random enemy encounters, and the rest of the automatically generated content that changes every time you might play the game.&lt;/p&gt; &lt;p&gt;We are also focused on automatic generation here and not static generation. Games like Halo, Fable, and the other big guys, don't leave room for error. They have someone hand-design each of the levels. At times they use automatic procedures to get a hint at what a level is going to look like, but they most often drastically modify this hint into the final product.&lt;/p&gt; &lt;p&gt;The game Elite was capable of generating enough data dynamically to consume the memory of today's machines, but was able to run in a much smaller working set (32K ;-). These guys really used world creation algorithms to generate something from nothing, when they really had nothing to start from but the most minimalistic code. That is all that would fit on the tape drives and even then you wanted your game to be up and running without waiting hours to be loaded into memory.&lt;/p&gt; &lt;p&gt;The first resource for this type of examination is a really poor example and I don't recommend it for all but the most complete game programming book collectors. &lt;a href="http://www.amazon.com/exec/obidos/ASIN/1584500581/justinsblog0b-20"&gt;Infinite Game Universe: Mathematical Techniques (Advances in Computer Graphics and Game Development)&lt;/a&gt; starts out slow, reads slow, ends slow, and&amp;nbsp;doesn't provide nearly the information that you'd think it should. The latest reviews on Amazon are ultra poor. At $35 discounted price it is a buy only if you are interested in several historic techniques and not necessarily the latest techniques for automatic world creation. Also note that there may not be any latest and greatest techniques for world generation, and instead most groups use either a pure math or brute force approach. Both are possible on today's machines.&lt;/p&gt; &lt;p&gt;Sadly enough I got to right here, swearing I had at least two more resources on my shelf. You know what? That isn't the cast. That is the only resource fully dedicated to more than a single aspect of world building. Since that is the case, I'll go ahead and plug a great terrain generation book &lt;a href="http://www.amazon.com/exec/obidos/ASIN/1584502045/justinsblog0b-20"&gt;Real-Time 3D Terrain Engines Using C++ and DirectX 9 (Game Development Series)&lt;/a&gt;. This is definitely a meaty book. All of the code is in C++, but easily translated into C#. In fact if enough people pull my leg I'll drop a source zip somewhere of nearly the entire book translated over into C#. I'm not sure of the legality of such practices, so I'll actually format an appropriate email to Greg and the publisher before I go and do something that might get me in trouble.&lt;/p&gt; &lt;p&gt;Ah ha, I knew I'd find another resource, but again, mainly terrain based (which is probably why it took me so long to find)... It was also in an AI book, &lt;a href="http://www.amazon.com/exec/obidos/ASIN/1584502894/justinsblog0b-20"&gt;AI Game Programming Wisdom 2&lt;/a&gt;, making it that much more difficult to just pull off of a shelf. One article (7.4) details the process involved in creating the random maps in Empire Earth. There is actually some good world building knowledge there, even though you are just creating a 2D map... Further, article 7.6 details the process of creating walls. Now, as done during part of a strategy you might not consider this world generation, but if done before-hand you would. I'm inclined to say this is definitely world-generation even if it is a run-time modification.&lt;/p&gt; &lt;p&gt;That is what I have for you. In terms of managed resources, we released the Terrarium world building code at some point, but I can't seem to find where we did that exactly. Most of the information is for mapping a world, specifically a height field, back to a 2 dimensional display. We originally did Isometric tiles, but the latest version run using larger square tiles. .Netterpillar appears to have some world generation code and I'll gladly provide a link to any discussion of that particular code if David tosses it online and maybe shows off a few enhancements? I think that would be extremely awesome.&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=241646" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/default.aspx">Terrarium</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Games4+.NET/default.aspx">Games4 .NET</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Algorithms/default.aspx">Algorithms</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Books+and+Publishing/default.aspx">Books and Publishing</category></item><item><title>Terrarium: How do we punish creatures that take too much time?</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/03/29/100949.aspx</link><pubDate>Mon, 29 Mar 2004 08:02:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:100949</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=100949</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/03/29/100949.aspx#comments</comments><description>&lt;P&gt;Okay, so Darren Neimke thinks this deserves a blog entry and I think he is probably correct.&amp;nbsp; The method for punishing creatures that take up too much time really isn't understood and the methods are actually somewhat complicated, so they deserve to be examined.&amp;nbsp; I talked about the Terrarium game loop in an earlier discussion that you can view here with specific comments about punishing creatures time-wise, without explaining how, &lt;A href="http://weblogs.asp.net/justin_rogers/archive/2004/03/21/93653.aspx"&gt;&amp;#8220;Terrarium: A focus on game loops and scheduling&amp;#8221;&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;The first thing we do is time an average creature.&amp;nbsp; We assume that an average creature will do specific amounts of specific things.&amp;nbsp; Right now those things include adding/removing items from collection, processing collections in loops, and making mathematical decisions.&amp;nbsp; We do this using three methods:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;InitAI - Simulates initializing data storage for AI components.&amp;nbsp; Here we basically create all of the collection storage and create a sample organism state that defines our creature for later comparison.&lt;/LI&gt;
&lt;LI&gt;DistanceChecks - Assume the creature will do a certain number of distance checks.&amp;nbsp; Currently we time 50 complex distance checks.&amp;nbsp; If you optimize your distance checks you could easily quadruple this number.&amp;nbsp; We end up making a bunch of calls to rand to create the random locations, and then compute a solid square root using the Math.Sqrt function. (You can use an approximation to speed things up here).&lt;/LI&gt;
&lt;LI&gt;StoreInformation - StoreInformation stores a bunch of random creatures and plants into varying arrays depending on whether or not they are friend or foe.&amp;nbsp; This again lets us do a series of additional distance checks to determine the closest creature.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;We do this a few times to warm-up the GC and get the methods JIT'ed.&amp;nbsp; Then we clear the timing information and run these tests 100 times so we can get the average.&amp;nbsp; Depending on the timing returned, creatures will on average get about 2 ms to operate their tick based on the timings that we do.&amp;nbsp; There is a kind of minimum timing that creatures will get and a theoretical maximum, that causes an assertion that only happens if you break in the debugger during this method (or you have a horked run-time since you work at MS and you just compiled yourself a bug in your code ;-).&lt;/P&gt;
&lt;P&gt;We use the base-time to determine how many creatures your computer should run as well, since we want to fit all of the creatures within about 300 milliseconds or 3/5ths of a game tick.&amp;nbsp; That is why some machines have smaller world sizes than others.&amp;nbsp; The maximum timings for creatures dictate this size, even if creatures only use half the time, we still don't allow more to operate since if the creatures did use the time it would elongate the game tick and cause choppy speed artifacts.&lt;/P&gt;
&lt;P&gt;Now for the fun stuff.&amp;nbsp; When the creature gets run, we time them to make sure they don't go over their maximum time allowance.&amp;nbsp; We do this unobtrusively, so that we don't break creatures that are only going over by a little bit.&amp;nbsp; We never interrupt the creature unless they go really over the time limit.&amp;nbsp; In fact, the deadlock detection routine allows for up to a couple of seconds before the creature actually gets blacklisted using the deadlock routine.&amp;nbsp; Blacklisting is really bad, since we zero out the creature's assembly and it no longer loads or allows that creature to participate.&lt;/P&gt;
&lt;P&gt;Under normal operation, creatures actually do return though, and we check the amount of time they spent processing against the allowed quantum.&amp;nbsp; Depending on the state of things, we add points to a special overage variable or subtract them off if they didn't use all of their time.&amp;nbsp; Overage is a number between 0 and some positive amount, and we never let you have a negative overage amount that allows you to essentially process longer in the future because you didn't use your time earlier.&lt;/P&gt;
&lt;P&gt;At the beginning of the following ticks, if your overage ever exceeds _maxOverage (something like a couple times the quantum), then we process your tick without you.&amp;nbsp; Basically, the creature class processes it's portion of the tick, but you don't get called.&amp;nbsp; You can check if you get skipped at any time by checking the TurnsSkipped property on your creature.&amp;nbsp; Unfortunately overage is stored within the game engine itself, so you can't really check if you are operating too long until you've been skipped at least once and been notified using the TurnsSkipped property.&lt;/P&gt;
&lt;P&gt;Note you have to check this property every time you run your code because it does get reset if callbacks into your code are made.&amp;nbsp; This is to ensure that it only holds value when you need to know how many ticks have elapsed since the last time you got to process a turn.&amp;nbsp; This can be important since various actions may have completed and you'll never get the event handler callbacks for them (they were processed while you were taking a processing nap for being naughty ;-)&lt;/P&gt;
&lt;P&gt;A large number of developers have expressed concern that they don't know how much time they have left or how much overage they used.&amp;nbsp; Our response is that the Terrarium is not about maximizing the amount of time you have available to you, and it isn't about managing your overage allowing you to think longer than you should some ticks and then buy your overage back down on other ticks.&amp;nbsp; It is simply about writing simple and effective algorithms to solve the problem space provided to your buy the Terrarium environment and other creature authors.&amp;nbsp; Note, the rules for stopping the processing of a creature would have to be quite a bit more strict if we did give out the overage information.&amp;nbsp; There are a number of gameplay attacks and tick elongation issues that developers could introduce and then completely work around the detection logic.&amp;nbsp; That would be extremely bad.&lt;/P&gt;
&lt;P&gt;To wrap up:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Your creature is granted a time quanta within the Terrarium based on tuning metrics of the average creature developed very early in the Terrarium process.&amp;nbsp; This tuning logic has worked for a long time now and is an effective method for load balancing the number of creatures per machine and the amount of time they should get to run.&lt;/LI&gt;
&lt;LI&gt;Creatures that hang can cause deadlocks.&amp;nbsp; These creatures are removed from the system (the entire species, not just the instance), and never allowed to run on the machine again (assuming Ecosystem mode).&lt;/LI&gt;
&lt;LI&gt;Creatures that go over their time by small amounts here and there are never affected.&amp;nbsp; These creatures end up buying back their overage by going under the allowed quanta on some ticks.&amp;nbsp; This is how the average creature functions in the Terrarium.&lt;/LI&gt;
&lt;LI&gt;Creatures that go over consistently eventually have their ticks skipped.&amp;nbsp; These creatures can determine that they are being bad by checking the TicksSkipped propery in their code to determine if they have been skipped during the previous tick(s).&amp;nbsp; This can be used to custom tune depth in path finding algorithms, shrink problem spaces, or provide better maximums for collections that are being processed.&lt;/LI&gt;
&lt;LI&gt;Giving programmatic access to overage variables means possible attacks against the scheduling logic that would allow for UI/gameplay issues to arise.&amp;nbsp; To combat these attacks the Terrarium would need to employ extremely strict rules on how creatures are run, and the liberal allowance of overage currently allowed that works so well would have to be revoked and replaced by a system that didn't allow nearly as much overage.&amp;nbsp; The threading system would also have to be replaced with a much stricter system that would, in many cases, break into user code while it was running and short-circuit user code execution paths.&amp;nbsp; This would place a great deal of stress on the user to write code that would recover from such a situation, along with numerous gameplay issues since the creature wouldn't complete it's tick.&lt;/LI&gt;&lt;/UL&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=100949" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/default.aspx">Terrarium</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Games4+.NET/default.aspx">Games4 .NET</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Algorithms/default.aspx">Algorithms</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Software+Design/default.aspx">Software Design</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Artificial+Intelligence/default.aspx">Artificial Intelligence</category></item><item><title>Taking a closer look at the power provided in stack based finite-state machines.</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/03/24/95711.aspx</link><pubDate>Thu, 25 Mar 2004 02:52:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:95711</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=95711</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/03/24/95711.aspx#comments</comments><description>&lt;P&gt;&lt;STRONG&gt;Abstract:&lt;BR&gt;&lt;/STRONG&gt;I've blogged before on the various types of finite-state machines you could possibly use in the Terrarium, but I didn't really go into any depth on how you might take advantage of them.&amp;nbsp; So I'm going to take this opportunity to talk about stack based finite-state machines, how your engine can quickly and easily interact with them, and propose an extension that allows for nested finite-state machines.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Requirements for a stack based FSM:&lt;/STRONG&gt;&lt;BR&gt;Well, the largest requirement is a stack where you can store state information.&amp;nbsp; You can either have the engine store this stack for you, or allow the FSM to store the stack.&amp;nbsp; I'm going to assume that the FSM is going to hold onto the stack for now, but we'll have to add some new rules later for nested state machines to operate properly.&amp;nbsp; Since this is the .NET world I am going to take full advantage of delegates to implement our state machine.&amp;nbsp; Each state will consist of a method callback and an optional data pointer to store extra data.&amp;nbsp; We'll use an object for this, but you could strongly type these if you wanted to.&lt;/P&gt;
&lt;P&gt;We'll encapsulate all of this data into an abstract base class that we'll expect each FSM to inherit from.&amp;nbsp; This allows us to create a strong contract with the engine, so it won't be left wondering what to do once the FSM is added to it's queue.&amp;nbsp; Our base class will provide access to the stack, provide a default&amp;nbsp;idle implementation, and in the future could perform other important operations.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;using System;&lt;BR&gt;using System.Collections;&lt;/P&gt;
&lt;P&gt;public delegate void StackFSMStateCallback(object extraData);&lt;BR&gt;public class StackFSMState {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public StackFSMStateCallback callback = null;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public object extraData = null;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public StackFSMState(StackFSMStateCallback callback, object extraData) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.callback = callback;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.extraData = extraData;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;
&lt;P&gt;public abstract class StackFSM {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private Stack stateStack = new Stack();&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public StackFSMState GetNextState() {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( stateStack.Count &amp;gt; 0 ) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (StackFSMState) stateStack.Pop();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new StackFSMState(new StackFSMStateCallback(this.Idle), null);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected void SetNextState(StackFSMState state) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; stateStack.Push(state);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public virtual void Idle(object extraData) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetNextState(new StackFSMState(new StackFSMStateCallback(this.Idle), null));&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;&lt;STRONG&gt;Creating our first FSM based on the stack:&lt;/STRONG&gt;&lt;BR&gt;Creating a new FSM based on the stack implementation is pretty easy.&amp;nbsp; We just need to override the Idle state method and provide our own implementation.&amp;nbsp; What would happen if we just wanted to randomly pop a new state onto the stack to execute 10% of the time.&amp;nbsp; This would actually be pretty easy.&amp;nbsp; BasicFSM does just that.&amp;nbsp; We are adding Console.WriteLine calls so we can understand which states are actually executing.&amp;nbsp; If we are in Idle we'll print that we are in that state, else we'll print we are in the Basic state.&amp;nbsp; Notice that I'm calling base.Idle whenever I'm not doing any processing.&amp;nbsp; This isn't required, but since base.Idle simply pops a new Idle state onto the stack, I might as well use it.&amp;nbsp; I could just as easily pop the state onto the stack myself, or simply not pop anything at all, since the GetNextState method makes sure we always return a valid state event.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;public class BasicFSM : StackFSM {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Random rand = new Random();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public override void Idle(object extraData) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Running an Idle state");&lt;/P&gt;
&lt;P dir=ltr&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( rand.Next(0, 10) == 5 ) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetNextState(new StackFSMState(new StackFSMStateCallback(this.BasicState), null));&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; base.Idle(extraData);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public virtual void BasicState(object extraData) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Running a basic state");&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;To demonstrate the rest of the code, let's change BasicFSM.Idle so that we randomly select which method we use to pop state back onto the stack.&amp;nbsp; We'll call this one UnsureFSM since we might run into this situation if a developer was unsure as to what they needed to do.&amp;nbsp; Or a tester might throw this code in to try and whack our state machine implementation.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;public class UnsureFSM : StackFSM {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Random rand = new Random();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public override void Idle(object extraData) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Running an Idle state");&lt;/P&gt;
&lt;P dir=ltr&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( rand.Next(0, 10) == 5 ) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetNextState(new StackFSMState(new StackFSMStateCallback(this.BasicState), null));&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; switch(rand.Next(0, 3)) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case 0:&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; base.Idle(extraData);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case 1:&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case 2:&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetNextState(new StackFSMState(new StackFSMStateCallback(this.Idle), null));&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public virtual void BasicState(object extraData) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Running a basic state");&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;&lt;STRONG&gt;Implementing a basic engine:&lt;BR&gt;&lt;/STRONG&gt;If you take all of the code up to now, you'll get a basic FSM implementation, but no engine to run it.&amp;nbsp; Since I want you to be able to test the process I am going to add a basic engine that calls into our FSM based on whatever the current state might be.&amp;nbsp; This'll be a basic queue for now where we add each of our FSM implementations, but you'll probably want a full on game scheduler running this at a later time.&amp;nbsp; I'll definitely be talking about game schedulers in the future so stay tuned.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;public class FSMEngine {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static void Main(string[] args) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Queue fsmQueue = new Queue();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fsmQueue.Enqueue(new BasicFSM());&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fsmQueue.Enqueue(new UnsureFSM());&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; while(true) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; StackFSM fsm = (StackFSM) fsmQueue.Dequeue();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; StackFSMState state = fsm.GetNextState();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; state.Callback(state.ExtraData);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fsmQueue.Enqueue(fsm);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Thread.Sleep(500);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;Compile all this code into an executable and run it.&amp;nbsp; You should get some pretty interesting results and you'll be able to play with the concepts involved.&lt;/P&gt;
&lt;P dir=ltr&gt;&lt;STRONG&gt;Exercising the stack:&lt;BR&gt;&lt;/STRONG&gt;So far we aren't exercising the stack.&amp;nbsp; What would it take to really exercise a stack based FSM?&amp;nbsp; Well, states that push multiple states onto the stack of course.&amp;nbsp; We basically need to break out of the push a new state on, pop it off next turn, rinse, repeat.&amp;nbsp; The one thing that is really good at defining multiple states is a movement algorithm.&amp;nbsp; These types of algorithms generally define multiple waypoints to be tackled along your path to the destination.&amp;nbsp; We can also exercise the code-path where we push a state back onto the stack because it isn't complete yet.&lt;/P&gt;
&lt;P dir=ltr&gt;So the MovementFSM starts in the Idle event and decides that 50 percent of the time it'll pick a target.&amp;nbsp; If no target it selected it pops on a new idle state, however, if we grab a target to move towards, then we have to extrapolate the points along the path that we'll be moving towards.&amp;nbsp; The ExtrapolateWayPoints handles this task for us by computing a random number of intermediate points and then popping these onto the stack.&amp;nbsp; We compute the points from closest to farthest away, and then push the on the stack in reverse order so the closest point is at the top of the stack.&lt;/P&gt;
&lt;P dir=ltr&gt;Once the movement points are on the stack, we fall out and see what happens.&amp;nbsp; Each point is moved towards in the Move state method.&amp;nbsp; We are now using the extraData to store the point we are moving towards, so we can print this out and see exactly where the FSM is moving.&amp;nbsp; At some point (10 percent of the time), we don't actually reach our destination waypoint, so we pop the waypoint back onto the stack.&amp;nbsp; This is an important refinement since it shows how easy it is to continue on our previous state with the same data.&amp;nbsp; If we didn't reach out location because it is unreachable, then we could even pop on additional waypoints to refine the path.&lt;/P&gt;
&lt;P dir=ltr&gt;One of the most powerful features of the stack based FSM is made apparent in the Move state method.&amp;nbsp; When the current state is complete, no work has to be done in order to transition to the next state.&amp;nbsp; Nothing new has to be done.&amp;nbsp; You can do new things, but you don't have to.&amp;nbsp; Removing this transition logic from certain algorithms makes them much easier to implement.&amp;nbsp; Here is the code:&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;public class MovementFSM : StackFSM {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Random rand = new Random();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public override void Idle(object extraData) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Running in Idle State: " + name);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Point target = PickTarget();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( target != Point.Empty ) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ExtrapolateWayPoints(target);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; base.Idle(extraData);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public virtual void Move(object extraData) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( extraData != null ) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Point wayPoint = (Point) extraData;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Moving towards " + wayPoint.ToString());&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( rand.Next(0, 10) == 1 ) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Move not completed&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetNextState(new StackFSMState(new StackFSMStateCallback(this.Move), extraData));&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // We are done, assume the next state is on the stack.&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Bonus! No transition required!&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private void ExtrapolateWayPoints(Point target) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Point[] wayPoints = new Point[rand.Next(1, 20)];&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Point start = new Point(rand.Next(0, 100), rand.Next(0, 100));&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int distanceX = target.X - start.X;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int distanceY = target.Y - start.Y;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for(int i = 0; i &amp;lt; wayPoints.Length - 1; i++) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; wayPoints[i] = new Point(&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; distanceX / wayPoints.Length * i + start.X,&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; distanceY / wayPoints.Length * i + start.Y&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; wayPoints[wayPoints.Length-1] = target;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Pop the way-points on in reverse order so the closest is on the top&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // of the stack.&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for(int i = wayPoints.Length - 1; i &amp;gt; -1; i--) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetNextState(new StackFSMState(new StackFSMStateCallback(this.Move), wayPoints[i]));&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private Point PickTarget() {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( rand.Next(0, 2) == 1 ) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new Point(rand.Next(0, 100), rand.Next(0, 100));&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return Point.Empty;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;&lt;STRONG&gt;Additional Features:&lt;BR&gt;&lt;/STRONG&gt;After a good deal of thought, I'm going to save the nested FSM implementation for an additional article.&amp;nbsp; There are many ways to implement nested FSMs with respect to where their stack state is stored and I'd like to thoroughly explain these.&amp;nbsp; I might also talk about stack control methods in the future and add that to the StackFSM implementation.&amp;nbsp; I think some worthy things to note would be stack compression (always a big subject), using fixed sized stacks, replacing stacks entirely and using your own strongly typed implementations, as well as some methods for better taking advantage of the extraData object.&lt;/P&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=95711" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/default.aspx">Terrarium</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Games4+.NET/default.aspx">Games4 .NET</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Quick+Tips/default.aspx">Quick Tips</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Algorithms/default.aspx">Algorithms</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Software+Design/default.aspx">Software Design</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Artificial+Intelligence/default.aspx">Artificial Intelligence</category></item><item><title>Terrarium: Enumerating collections with an outlook on how to speed up the process.</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/03/22/94223.aspx</link><pubDate>Tue, 23 Mar 2004 01:20:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:94223</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=94223</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/03/22/94223.aspx#comments</comments><description>&lt;P&gt;Okay, so I was going through some old source code, that I won't claim since I probably didn't write it based on the fact the as statement wasn't used (as soon as I found out about the as statement I made sure to take advantage of its usage whenever possible) and I found the following code.&amp;nbsp; Note this is pretty old code, but I'll use it anyway.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;OrganismState organismState = newWorldState.GetOrganismState(organismID);&lt;/P&gt;
&lt;P&gt;if (organismState != null &amp;amp;&amp;amp; organismState.GetType() == typeof(PlantState))&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PlantState plantState = (PlantState) organismState;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int availableLight = CurrentVector.State.GetAvailableLight(plantState);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; plantState.GiveEnergy(availableLight);&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;Okay, now this is a prime example of some code that should have used the as statement.&amp;nbsp; So what is happening in the above code?&amp;nbsp; Well, we are doing a null check, then we are doing a GetType() call, and check that versus a target type.&amp;nbsp; After all this we perform a cast and finally start to work on the target.&amp;nbsp; How could this code be simplified?&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;PlantState plantState = newWorldState.GetOrganismState(organismID) as PlantState;&lt;/P&gt;
&lt;P dir=ltr&gt;if ( plantState != null )&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int availableLight = CurrentVector.State.GetAvailableLight(plantState);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; plantState.GiveEnergy(availableLight);&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;That simplifies things quite a bit and is a little faster.&amp;nbsp; Unfortunately we have little pieces of the original all over the place in the code, so updating things becomes a hassle (assume that the original way was the only way to do it for some reason) whenever we change the way we perform certain operations (aka iterating over a collection of various objects where we only want specific ones based on a type).&amp;nbsp; Here things like pre-sorting can greatly enhance the locality of the type checking operation, and at the same time allow you to quickly change the way sorting happens if your code changes a bit.&amp;nbsp; It also allows you to prevent the type checking for the same item multiple times.&amp;nbsp; This can be pretty powerful.&amp;nbsp; Let's examine the collections we might use in order to simplify our design.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;// We know there are Animals and Plants, both of which are derived from Organism&lt;BR&gt;// so we are going to role all this data up early for use in all of our&lt;BR&gt;// methods.&lt;/P&gt;
&lt;P dir=ltr&gt;int animals = 0;&lt;BR&gt;int plants = 0;&lt;/P&gt;
&lt;P dir=ltr&gt;// Pre-count&lt;BR&gt;for(int i = 0; i &amp;lt; State.Organisms.Length; i++)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( State.Organisms[i] is PlantState )&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; plants++;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; else if ( State.Organisms[i] is AnimalState )&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; animals++;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;
&lt;P dir=ltr&gt;OrganismState[] oStates = new OrganismState[State.Organisms.Length];&lt;BR&gt;PlantState[] pStates = new PlantState[plants];&lt;BR&gt;AnimalState[] aStates = new AnimalState[animals];&lt;/P&gt;
&lt;P dir=ltr&gt;for(int i = 0, j = 0, k = 0; i &amp;lt; State.Organisms.Length; i++)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; oStates[i] = (OrganismState) State.Organisms[i];&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( State.Organisms[i] is PlantState )&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStates[j++] = (PlantState) State.Organisms[i];&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; else if ( State.Organisms[i] is AnimalState )&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; aStates[k++] = (AnimalState) State.Organisms[i];&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;I want to place a note here about something.&amp;nbsp; Notice, I'm using casting instead of the as keyword.&amp;nbsp; You see, the difference is that I'm already pre-checking the value using the is keyword.&amp;nbsp; I don't want to incur the wrath of extra work (as does some extra work checking the type before casting) twice in this case.&amp;nbsp; This demonstrates that depending on your casting process and where you need various pieces of information in order to make your casting decisions, you'll need to change which casting methods you use.&lt;/P&gt;
&lt;P dir=ltr&gt;Now our original method changes a bit and becomes highly simplified.&amp;nbsp; Rather than doing the casting and iterating over an array where up to half of the elements might not even match the correct type, we've already done the work of pre-sorting, so we can index into our plant state array directly.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;for(int i = 0; i &amp;lt; pStates.Length; i++)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int availableLight = CurrentVector.State.GetAvailableLight(pStates[i]);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pStates[i].GiveEnergy(availableLight);&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;A final optimization has been pointed out several times in other blogs.&amp;nbsp; There is no reason to incur the wrath of a local variable such as availableLight if we don't need it.&amp;nbsp; It is possible to instead change the method GetAvailableLight to simply apply the lighting values directly to the plant state in question.&amp;nbsp; I mean, why would we, when it simplifies the design process (taking away some flexibility).&amp;nbsp; The final method might look something like:&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;for(int i = 0; i &amp;lt; pStates.Length; i++)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; CurrentVector.State.GiveEnergyByLight(pStates[i]);&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;Only use this final optimization where it makes sense.&amp;nbsp; Perhaps do some tuning and see if it actually buys you anything.&lt;/P&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=94223" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/default.aspx">Terrarium</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Games4+.NET/default.aspx">Games4 .NET</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Quick+Tips/default.aspx">Quick Tips</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Performance/default.aspx">Performance</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Algorithms/default.aspx">Algorithms</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Software+Design/default.aspx">Software Design</category></item><item><title>Terrarium: A path-finding dream/nightmare...</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/03/04/84246.aspx</link><pubDate>Fri, 05 Mar 2004 03:52:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:84246</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=84246</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/03/04/84246.aspx#comments</comments><description>&lt;P&gt;Path-finding in the Terrarium is a classic David versus Goliath problem.&amp;nbsp; While the groundwork for enabling path-finding experimentation in 2D was always there, some of the implementation details tended to make it hard for people to try out new ideas.&amp;nbsp; What are some of the great features in the Terrarium with regards to path-finding?&lt;/P&gt;
&lt;OL&gt;
&lt;OL&gt;
&lt;LI&gt;A basic 2D movement system using basic waypoints to start movement and a movement completed callback system.&lt;/LI&gt;
&lt;LI&gt;Static terrain elements in the form of plants.&lt;/LI&gt;
&lt;LI&gt;Dynamic terrain elements in the form of other creatures.&lt;/LI&gt;
&lt;LI&gt;The callback system implements a blocked routine so you can find out if your movement completed 100% or you got blocked along the way.&lt;/LI&gt;&lt;/OL&gt;&lt;/OL&gt;
&lt;P&gt;I think we got these parts right.&amp;nbsp; Movement in the Terrarium is as easy as a MoveTo(Point).&amp;nbsp; Your creature will make a straight line path for that point.&amp;nbsp; If you run into something you can try to move around it or provide whatever algorithm you'd like.&amp;nbsp; This means algorithms can be completely reactive without any form of prediction (reactive by responding to blocking events, rather than predictive in trying to plot the optimum path up front).&lt;/P&gt;
&lt;P&gt;We also got some things wrong in the path-finding implementation.&amp;nbsp; These are things that either made it difficult to implement precise path-finding or left developers confused.&lt;/P&gt;
&lt;OL&gt;
&lt;OL&gt;
&lt;LI&gt;We used a grid cell system that encompassed an 8x8 pixel area.&amp;nbsp; Each creature took up a grid area that was used to compute blocking.&lt;/LI&gt;
&lt;LI&gt;The line drawing algorithm used internally made it hard to diagonally navigate by blocked cells.&amp;nbsp; Even after publishing the algorithm, some authors had trouble with navigating terrain and randomly getting blocked even though they felt they shouldn't.&lt;/LI&gt;
&lt;LI&gt;We never provided any helper algorithms for path-finding.&amp;nbsp; This was just a lack of time on our part.&lt;/LI&gt;
&lt;LI&gt;Our Vector implementation had a poor Sqrt implementation.&amp;nbsp; We used an approximation that was as much as 10% off at times.&amp;nbsp; Yuck, that isn't very accurate at all.&lt;/LI&gt;&lt;/OL&gt;&lt;/OL&gt;
&lt;P&gt;Even with all of the issues with the path-finding system, we had thousands upon thousands of creature authors (We love you guys!).&amp;nbsp; Every author had a slightly different approach to path-finding I'm sure, but I can identify several classes of implementation.&amp;nbsp; I'd like to do this, because it shows how Terrarium was an excellent environment for spec'ing out new and interesting ideas.&lt;/P&gt;
&lt;OL&gt;
&lt;OL&gt;
&lt;LI&gt;A*.&amp;nbsp; Go figure right?&amp;nbsp; Of course the most popular robotics path-finding algorithm would be implemented at some point.&amp;nbsp; I think most of the creatures wound up using something like A* to do the path-finding.&lt;/LI&gt;
&lt;LI&gt;Divide and Conquer (for lack of a better name).&amp;nbsp; This is your classic recursive refinement algorithm.&amp;nbsp; Plot waypoints which recursively define a more refined path when a blocking agent is in the way.&amp;nbsp; I used this most often, because it was fast, I could spread the refinement over multiple game ticks, and I never had to worry about using too much processor time.&lt;/LI&gt;
&lt;LI&gt;Reactive path-finding.&amp;nbsp; This was the simplest algorithm we ever saw.&amp;nbsp; Basically, move blindly to a point.&amp;nbsp; If you get blocked then try to move in reverse vectors for some period of time to try and get around the blocking agent.&amp;nbsp; Eric Zinda, the brain behind the original Terrarium, was the first author that used this approach and it worked quite well for most developers during the initial release of the Terrarium.&lt;/LI&gt;
&lt;LI&gt;Gravity path-finding.&amp;nbsp; I loved talking about and implementing this approach.&amp;nbsp; Basically, every agent in the system defines some degree or aura of repulsion.&amp;nbsp; This resulted in very organic path-finding with creatures taking arc'ed curves around blocking agents to try and get back on track.&amp;nbsp; Gravity was defined by the threat a given blocking agent defined, so that you might be repulsed very early by carnivores if you were a herbivore, or you might even be drawn to a plant.&amp;nbsp; A better name for this might be Magnetic path-finding or even influence mapping (this is the technical name for it), but the original note describing this algorithm used gravity, so I'll stick with that to honor the original idea.&lt;/LI&gt;
&lt;LI&gt;Influence Mapping.&amp;nbsp; I think this deserves it's own section as well.&amp;nbsp; All of the above algorithms can be enhanced using influence mapping.&amp;nbsp; At some point you have to decide where to move and influence mapping can help make that decision, as well as influencing how you get there.&amp;nbsp; For A* there is a concept of a cost for each cell you move through and by influence mapping an area you can provide better costing for cells based on whether or not there is a threat or benefit in a given area.&amp;nbsp; (I want to move to X, but if I can eat plants along the way, then that is cool too, even if I have to move a bit further).&lt;/LI&gt;&lt;/OL&gt;&lt;/OL&gt;
&lt;P&gt;Moving forward I think the Terrarium source code will enable AI developers to better define arenas for testing various forms of path-finding.&amp;nbsp; Changing the grid cell algorithm out for a radius algorithm would be the first order of business and would allow for much more organic movement rather than the square movement of the current Terrarium.&amp;nbsp; Using a more solid vector class and other helper libraries will enable much more rapid prototyping of higher level algorithms.&amp;nbsp; Being able to modify the environment and provide more types of simulation (weight mapping the terrain using height values) will enable more complex terrain to navigate making more specialized path-finding algorithms better suited to navigating a Terrarium than the generic algorithms currently used (currently time is a big issue and the most basic algorithms tend to be the most highly used.&amp;nbsp; With some small changes to the code-base more specialized and time consuming algorithms may become more important and better suited than the simple/generic methods of today).&lt;/P&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=84246" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/default.aspx">Terrarium</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Games4+.NET/default.aspx">Games4 .NET</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Algorithms/default.aspx">Algorithms</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Artificial+Intelligence/default.aspx">Artificial Intelligence</category></item></channel></rss>