XNA Creators Club Online
Page 1 of 1 (9 items)
Sort Posts: Previous Next

XML: reading expressions and variables

Last post 26/08/2009 22:20 by Lintford Pickle. 8 replies.
  • 04/07/2009 0:03

    XML: reading expressions and variables

    Hi,

    Well I am currently looking into creating a story generator for my project, using XML.  The idea so far is to sequenially parse through an XML file, checking conditions and creating/modifing variables based on different elements which add a snippet to the overral story.  I have seen a project on the internet which uses this method, but no source code is available.  Here is an example of a typical element within the XML file ...


     
    <root> 
        <Name>StartPoint</Name> 
        <condition>playerMoney >= 500;</condition> 
        <operations> 
            playerIsRich = true
            playerMoney = 1000
        </operations> 
        <Text>text to add to the story generator should the above condition be passed.</Text> 
        <Next>next element in the generation chain</Next> 
    </root> 
     

    There are currently two 'problems' which I have encountered here,

    the first is, how would it be possible to read and evaluate the expression contained within <condition>. 
    and second is, how/is it possible to read the <operation> attributes and create variables from them in code (the problem being how do you define variable names at runtime)?

    At the minute I am thinking along the lines of parsing the text for specific values (e.g. ' = true' or '= false') and based on this create a new instance in a dictionary.  meaning i would need one dictionary for bool, int, string etc.

    Failing that, I'm guessing it would require using some scripting language (atm I am just aiming for a PC version of the story generator). 

    Maybe there is an easier way of doing this, so any input would be appreciated :)

    -John




  • 04/07/2009 3:22 In reply to

    Re: XML: reading expressions and variables

    Howdy,

    Both problems are solved with the same technique! Have alook at Expression Trees they are very neat and powerful. You will also need ExpressionTree Serialization to safe them.

    If you define a standard input say an Interface that youapply to all actors in your system then you can design all your expressionsaround that interface.

    An example:

    var pActor = Expression.Parameter(typeof(IActor), "Money");
    var cMoney = Expression.Constant(500);
    var body = Expression.GreaterThanOrEqual(pActor, cMoney);
    var exp = Expression.Lambda<Func<IActor, bool>>(body, pActor);

    Then at run time you load the expression compile it and usethe Func<>



    if (lamdba(actor)) {}

    Take Care,

    Jamie

  • 04/07/2009 7:54 In reply to

    Re: XML: reading expressions and variables

    NEARFANTASTlCA:

    Howdy,

    Both problems are solved with the same technique! Have alook at Expression Trees they are very neat and powerful. You will also need ExpressionTree Serialization to safe them.

    If you define a standard input say an Interface that youapply to all actors in your system then you can design all your expressionsaround that interface.

    An example:

    var pActor = Expression.Parameter(typeof(IActor), "Money");
    var cMoney = Expression.Constant(500);
    var body = Expression.GreaterThanOrEqual(pActor, cMoney);
    var exp = Expression.Lambda<Func<IActor, bool>>(body, pActor);

    Then at run time you load the expression compile it and usethe Func<>



    if (lamdba(actor)) {}

    Take Care,

    Jamie


    Expression trees are indeed a great solution for this, but be warned that they do not work on the 360. If you need to make this work on the 360, you can get something resembling an equivalent implementation by layering delegates at runtime - it won't be nearly as efficient, but for many uses it can be good enough, and will work on both PC and 360. If you write your code correctly, you can still use expression trees on PC and simply use layered delegates as a fallback implementation on the 360.

    If it's not clear, here's the basic technique you can use with delegates to approximate expression trees:
    Func<intintint> int_Add = (lhs, rhs) => lhs + rhs; 
    Func<intintint> int_Multiply = (lhs, rhs) => lhs * rhs; 
     
    Func<int> myExpression = () => 
      int_Multiply(int_Add(1, 2), 3); 
    At runtime you can easily construct delegates this way by iteratively walking over the expression, layering delegates over each other. If performance becomes a concern you can trivially implement things like constant folding with this approach as well.
    Kevin Gadd, Squared Interactive
    Development Blog | Twitter
    Help playtest my game, Inferus!
  • 09/07/2009 20:41 In reply to

    Re: XML: reading expressions and variables

    Hi, and sorry for the late reply.

    I've read up on expression trees, and they are definately a very cool feature, which I admittedly hadn't seen before.

    I think it may take a little getting used too though, but thanks for the replies.

    Also, I'm still not 100% how expression trees / layering delegates could be used for the creation of variables at run-time which exist for the life(scope) of the writer's story, although I can see how to use each variable as part of an expression. 

    Suppose the writer introduced a new variable at the start of the story, playerIsHappy = true; in side the data file.  He could then go onto change this variable during the life of the story depend what random elements are selected.  Then at the very end of the story generation, he can define a condition to check if the story has an happy ending or not.

    I've probably just either miss-read or missed this on the MSDN, but a futher example would be appriciated.

    thanks

    -john
  • 10/07/2009 2:05 In reply to

    Re: XML: reading expressions and variables

    You would probably want to represent user variables in a data structure at runtime - like a dictionary keyed on variable name, or something like that. You can then look up variables in that dictionary when evaluating your expressions.
    Kevin Gadd, Squared Interactive
    Development Blog | Twitter
    Help playtest my game, Inferus!
  • 10/07/2009 3:04 In reply to

    Re: XML: reading expressions and variables

    You're effectively trying to serialize a state machine. Now some pieces are easier than others. Serializable operations and predicates are not among the easy parts. There's a wealthy of interesting solutions for windows deployment, but those won't all work on the compact framework. I would try to simplify the predicates and operations into a library of pre-existing commands (written in C#) and serialize the name or an index to the command into the XML with the parameters.

    And environmental parameters would work the same way, if your success condition is having more money than your rival, not only do you need to add a compareMoney expression to the command library, but you'd need a 2nd library of parameters where maybe [Your Rival] is ID 6 and you'd serialize 6 into the XML which at run-time will be resolved to reference your rival so the compareMoney command knows who to target.
  • 10/07/2009 4:52 In reply to

    Re: XML: reading expressions and variables

    System.Reflection is your friend!
    You probably want to have a look at the XNA Path code to get inspiration. That code evaluates expressions that follow properties and call functions, but doesn't (currently) have conditionals or math. You can have a function called "AddMoney()" and call it, though.

    Jon Watte, Direct3D MVP
    Tweets, occasionally
    kW X-port 3ds Max .X exporter
    kW Animation source code
  • 10/07/2009 6:21 In reply to

    Re: XML: reading expressions and variables

    jwatte:
    System.Reflection is your friend!
    You probably want to have a look at the XNA Path code to get inspiration. That code evaluates expressions that follow properties and call functions, but doesn't (currently) have conditionals or math. You can have a function called "AddMoney()" and call it, though.
    The technique described is also the one I'm using for expressions currently. There are slight garbage generation concerns, but you can work around them by doing some intelligent caching. The real advantage here is that this lets you add a hint of dynamic scripting to your games without having to go to the trouble of embedding a complex runtime. You can implement basic ternary conditionals ( condition ? true : false style ) and arithmetic without much effort or prior experience, which gets you 90% of the way there. In some cases that may be all you need for your game, since anything beyond that level of complexity should probably just be compiled into your C# anyway.

    The main concerns if you're using reflection is that each reflection call on a given type has a fairly large initial cost, and further calls tend to generate a large amount of garbage. Aggressively caching calls to things like GetMethod and GetField will help tremendously, but you're still pretty much guaranteed to generate a small amount of garbage from every call, since invoking methods and accessing fields via reflection requires* boxing value types.

    I described my approach in minimal detail here and the source code for the lexer is here. If it'd be of use to you I can move the expression compiler to Google Code as well (right now it's part of the source tree for my game since it's tightly integrated, but if it would be of help to you I don't mind moving it over.) I currently use it for basic conditionals and linking the states of objects, so things like:

                <SpriteAnimation typeId="1">
                    <Name>Grapple</Name>
                    <Group name="idle" />
                    <Frames delay="50" loop="PingPong" />
                    <Branches>
                        <Branch name="Stand to Crouch" if="!Grappling and (Crouching)" />
                        <Branch name="Walk" if="!Grappling and (Acceleration != 0)" />
                        <Branch name="Stand" if="!Grappling" />
                    </Branches>
                </SpriteAnimation>

    and

          <LevelMovingPlatform typeId="8">
            <Name>BottomMovingPlatform</Name>
            <Speed>BottomEndingZone.State ? 4.0 : BottomCrank.State</Speed>

    are handled by the expression parser so that I can tweak and adjust things in real-time without dealing with the compiler. (This is obviously a very slippery slope, though; when building a scripting system like this you're in continual danger of reinventing an entire wheel when you've already got a good one at your disposal.)

    Where possible I strongly encourage making your expression syntax very C#-like, if not exactly equivalent to C#. Doing so will save you a lot of stress later on if you decide that the overhead of runtime parsing/delegate combining/expression trees is too much, and you want to compile all your game's expressions at build time, because that will allow you to generate .cs files from them and build them into assemblies that you can ship with your game.

    * Note that in some cases you can bypass the need for boxing if you're willing to jump through some extremely elaborate hoops involving generic methods and the Delegate constructor:

            private static IValueProxy<T> WrapPropertyGetter<T> (Func<T> fn) {
                if (fn == null)
                    return null;

                return new TypedValueProxy<T>(fn);
            }

            public static IValueProxy MakePropertyGetter (object obj, PropertyInfo property) {
                var baseType = typeof(Func<>);
                var delegateType = baseType.MakeGenericType(property.PropertyType);
                var del = Delegate.CreateDelegate(delegateType, obj, property.GetGetMethod());
                var baseMethod = typeof(ExpressionCompiler).GetMethod("WrapPropertyGetter", BindingFlags.Static | BindingFlags.NonPublic);
                var wrapperMethod = baseMethod.MakeGenericMethod(property.PropertyType);
                var result = (IValueProxy)wrapperMethod.Invoke(null, new object[] { del });
                return result;
            }

    I love reflection. :D

    Kevin Gadd, Squared Interactive
    Development Blog | Twitter
    Help playtest my game, Inferus!
  • 26/08/2009 22:20 In reply to

    Re: XML: reading expressions and variables

    Hi,

    I just wanted to quickly say thanks for the help to Kevin and Jon. I still haven't got around to doing the procedural story generation, but I don't like leaving the thread unanswered for so long, especially after you both providing long detailed answers.

    Actually, both methods still seem rather inappropriate for what I had in mind, or rather, I think I could generate just as compelling stories without the  expression building and branching etc.

    When I do eventually finish it, I'll upload it somewhere and update this thread, so anyone who wants to see/use it can.

    thanks alot

    -john
Page 1 of 1 (9 items) Previous Next