the decision context translator for robocode

I have been implementing a bounded context for decision making for my robocode robot and continue in this post with the decision context translator - this will be responsible for converting data from the world state to the decision state dialect.

My specification starts with a new translator type and a new interface to interact with, the decision state updater:

    [Subject(typeof(DecisionContextTranslator))]
    public class when_publishing_a_state_change
    {
        Establish context = () =>
        {
            decisionStateUpdater = MockRepository.GenerateStub<IUpdateDecisionState>();
            decisionContextTranslator = new DecisionContextTranslator(decisionStateUpdater);

            worldState = MockRepository.GenerateStub<IWorldState>();
        };

        Because of = () => decisionContextTranslator.PublishAStateChange(worldState);

        It should_update_state_via_the_decision_state_updater = () =>
            decisionStateUpdater.AssertWasCalled(d => 
                                    d.UpdateState(Arg<Action<DecisionState>>.Is.NotNull));

        static IContextTranslator decisionContextTranslator;
        static IWorldState worldState;
        static IUpdateDecisionState decisionStateUpdater;
    }

I'm ensuring that the translator will provide an Action delegate to modify a decision state and the implementation of this class is very easy so far:

    public class DecisionContextTranslator : IContextTranslator
    {
        IUpdateDecisionState decisionStateUpdater;

        public DecisionContextTranslator(IUpdateDecisionState decisionStateUpdater)
        {
            this.decisionStateUpdater = decisionStateUpdater;
        }

        public void PublishAStateChange(IWorldState worldState)
        {
            decisionStateUpdater.UpdateState(d => { } );
        }
    }

I now need to flesh out the delegate with the requirements from the decision context - at present this is a mapping between turn numbers. In the future the dialects will hopefully be different enough to warrant this separate context.

The specification is changed significantly to introduce this:

    [Subject(typeof(DecisionContextTranslator))]
    public class when_publishing_a_state_change
    {
        Establish context = () =>
        {
            decisionStateUpdater = MockRepository.GenerateStub<IUpdateDecisionState>();
            decisionStateUpdater.Stub(d => d.UpdateState(null))
                                .IgnoreArguments()
                                .Do(RecordTheDecisionStateUpdate);

            decisionContextTranslator = new DecisionContextTranslator(decisionStateUpdater);

            turnNumber = 98235;
            worldState = MockRepository.GenerateStub<IWorldState>();
            worldState.CurrentTurnNumber = turnNumber;
        };

        Because of = () => decisionContextTranslator.PublishAStateChange(worldState);

        It should_update_state_via_the_decision_state_updater = () =>
            decisionStateUpdater.AssertWasCalled(d => 
                                      d.UpdateState(Arg<Action<DecisionState>>.Is.NotNull));

        It should_map_the_turn_number_from_world_state_to_decision_state = () =>
            TheDecisionStateHavingHadTheUpdateApplied().TheNumberOfTurnsSoFar
                                          .ShouldEqual(Convert.ToInt32(turnNumber));

        static DecisionState TheDecisionStateHavingHadTheUpdateApplied()
        {
            var decisionState = new DecisionState();
            if(decisionStateUpdate!=null)
                decisionStateUpdate(decisionState);

            return decisionState;
        }

        static Action<Action<DecisionState>> RecordTheDecisionStateUpdate = 
                                                      a => decisionStateUpdate = a;

        static IContextTranslator decisionContextTranslator;
        static long turnNumber;
        static IWorldState worldState;
        static IUpdateDecisionState decisionStateUpdater;
        static Action<DecisionState> decisionStateUpdate;
    }

First, the decision updater stub is set up to record the action that goes into the update call and I have a static Action member at the bottom that will record this in a member variable "decisionStateUpdate".
I also define a turn number on the world state that I can then observe against in my specification.

In my observation I apply any changes to a newly created decision state and I can then check if the turn number was updated.

Here is the implementation of this:

    public class DecisionContextTranslator : IContextTranslator
    {
        IUpdateDecisionState decisionStateUpdater;

        public DecisionContextTranslator(IUpdateDecisionState decisionStateUpdater)
        {
            this.decisionStateUpdater = decisionStateUpdater;
        }

        public void PublishAStateChange(IWorldState worldState)
        {
            decisionStateUpdater.UpdateState(d => d.TheNumberOfTurnsSoFar = 
                                                     (int)worldState.CurrentTurnNumber );
        }
    }

This is all the context translator has to do for the time being and I now need to move onto the decision state updater. I've chosen to implement this interface with the decision state keeper, so I need a new specification for that class:

    [Subject(typeof(DecisionStateKeeper))]
    public class when_provided_with_a_state_update
    {
        Establish context = () =>
        {
            decisionMakingObserver = MockRepository.GenerateStub<IObserveToMakeDecisions>();
            decisionObservers = new List<IObserveToMakeDecisions> { decisionMakingObserver };
            decisionStateKeeper = new DecisionStateKeeper(decisionObservers);

            decisionStateKeeper.Situation.Subscribe(s =>
                {
                    observedDecisionState = s.EventArgs;
                });

            theExpectedNumberOfTurns = 5918;
            decisionStateUpdate = d => d.TheNumberOfTurnsSoFar = theExpectedNumberOfTurns;
        };

        Because of = () => decisionStateKeeper.UpdateState(decisionStateUpdate);

        It should_update_the_situation_observable = () => 
observedDecisionState.ShouldNotBeNull(); It should_have_the_correct_decision_state_after_the_update = () => observedDecisionState.TheNumberOfTurnsSoFar.ShouldEqual(theExpectedNumberOfTurns); static DecisionStateKeeper decisionStateKeeper; static IEnumerable<IObserveToMakeDecisions> decisionObservers; static Action<DecisionState> decisionStateUpdate; static DecisionState observedDecisionState; static int theExpectedNumberOfTurns; static IObserveToMakeDecisions decisionMakingObserver; }

In this specification I'm creating a new decision state keeper and am subscribing to the situation observable so I can obtain the decision state when the observable is activated.
I can then assert that this decision state is available and that the number of turns so far is equal that provided in the proposed state update.

This changes the decision state keeper to the following:

    public class DecisionStateKeeper : IKeepDecisionState, IUpdateDecisionState
    {
        DecisionState internalDecisionState;

        public DecisionStateKeeper(IEnumerable<IObserveToMakeDecisions> decisionMakingObservers)
        {
            CreateObservableForDecisionState();

            foreach(var observer in decisionMakingObservers)
            {
                observer.Observe(this);
            }
        }

        public IObservable<IEvent<DecisionState>> Situation { get; private set; }

        event EventHandler<DecisionState> DecisionStateHasUpdated;

        void CreateObservableForDecisionState()
        {
            Situation = Observable.FromEvent<DecisionState>(
                ev => this.DecisionStateHasUpdated += ev,
                ev => this.DecisionStateHasUpdated -= ev);
        }

        public void UpdateState(Action<DecisionState> decisionStateUpdate)
        {
            decisionStateUpdate(InternalDecisionState);
            DecisionStateHasUpdated(this, InternalDecisionState);
        }

        protected DecisionState InternalDecisionState
        {
            get 
            {
                return internalDecisionState ?? (internalDecisionState = new DecisionState());
            }
        }
    }

I'm now almost ready and my next post will cover the final piece to introduce this new context and I'll be ready to run my acceptance tests once again.



0 comments: