marshcode / maze

C# Maze libraries and sample implementations
http://www.marshcode.com/?page_id=22
MIT License
0 stars 0 forks source link

Tresure Maze Cleanup #7

Open marshcode opened 11 years ago

marshcode commented 11 years ago

Several deficiencies exist that should be taken care of.

  1. TreasureMaze should provide an "add_treasure(Position p)" method that will remove the existing tile and add a treasure tile. Once finished, refactor the place_tile to use it and remove all HashSets outside of that class
  2. Refactor the maze generation items to accept an already created Maze as opposed to generating a static maze. Consider how to handle maze generation that needs to adjust the desired maze dimensions.
    1. Allow mazes to be resized (Hate it, I'm just listing it for completeness)
    2. Provide methods on the generators that will massage the coordinates for you. Then make assertions in the code instead of massaging. This requires you to always ask the generator in question what size the maze should be given the desired coordinates.

1 and 2 above should happen regardless of the outcome below.

Originally, this was to make it so TreasureMaze could be a true subclass of Maze. However, when considering future improvements (TrapMaze? MonsterMaze) the idea of MazeHandlers came to mind.

These wouldn't be a defined abstract or interface class. They would be a design pattern used in conjuction with subclassed Tiles, Walls and Characters.

They would add functionality to the maze and track state based on their enhancements without causing a combinatorial explosion.

On the surface, this might be a lazy man's Decorator pattern (we wouldn't have to implement a mostly pass-through interface on the container maze).

Alternately, we could implement an abstract pass-through maze to facilitate the creation of maze decorators. This would implement a true decorator pattern without burdening the programmer with implementing the entire maze signature every time.

marshcode commented 11 years ago

Additionally, consider adding a dynamic visitor to the code and integrating it into the Maze. Allowing the program to query the concrete tile objects would free the decorators above from keeping lists of specific tiles (see TreasureMaze? keeping TreasureTile? references in a list). Refer to the attached program to see how the visitor pattern can be implemented in C#;

marshcode commented 11 years ago

Visitor Pattern in C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{

    public class Letter{
        public string do_letter()
        {
            return "specific for Letter";
        }

    }

    public class A : Letter {

        public string do_a()
        {
            return "specific for A";
        }

    }
    public class B : Letter {

        public string do_b()
        {
            return "specific for B";
        }

    }

    public class C : Letter
    {
        public string do_c()
        {
            return "specific for C";
        }
    }

    public interface IActionVisitor<in TBase>
        where TBase : class
    {
        /// <summary>
        /// Register action on <see cref="T"/>.
        /// </summary>
        /// <typeparam name="T">Concrete type.</typeparam>
        /// <param name="action">Action.</param>
        void Register<T>(Action<T> action)
            where T : TBase;

        /// <summary>
        /// Visit concrete type.
        /// </summary>
        /// <param name="value">Type to visit.</param>
        void Visit<T>(T value)
            where T : TBase;
    }

    public static class Visitor
    {
        /// <summary>
        /// Create <see cref="IActionVisitor{TBase}"/>.
        /// </summary>
        /// <typeparam name="TBase">Base type.</typeparam>
        /// <returns>New instance of <see cref="IActionVisitor{TBase}"/>.</returns>
        public static IActionVisitor<TBase> For<TBase>(Action<object> default_action=null)
            where TBase : class
        {
            return new ActionVisitor<TBase>(default_action);
        }

        private sealed class ActionVisitor<TBase> : IActionVisitor<TBase>
            where TBase : class
        {
            private readonly Dictionary<Type, dynamic> _repository;
            private readonly Action<object> default_action;

            private void default_action_f(object o){}

            public ActionVisitor(Action<object> default_action=null)
            {
                this._repository = new Dictionary<Type, dynamic>();
                this.default_action = default_action;

            }

            public void Visit<T>(T value)
                where T : TBase
            {
                Type key = value.GetType();
                dynamic action;
                action = this.default_action;
                if (_repository.ContainsKey(key))
                {
                    action = _repository[key];
                }

                if (action != null)
                {
                    action((dynamic)value);
                }
            }

            public void Register<T>(Action<T> action)
                where T : TBase
            {
                _repository[typeof(T)] = action;
            }
        }
    }

    public class Worker
    {

        public int num_a_visited = 0;
        public int num_b_visited = 0;
        public int num_letter_visited = 0;
        public int num_unknown_visited = 0;

        public Worker()
        {
        }

        public void visit_unknown(object o)
        {
            Console.WriteLine(String.Format("Unregistered type: {0}", o.GetType().ToString()));
            this.num_unknown_visited += 1;
        }

        public void visit_letter(Letter letter)
        {
            Console.WriteLine(letter.do_letter());
            this.num_letter_visited += 1;
        }
        public void visit_a(A a)
        {
            Console.WriteLine(a.do_a());
            this.num_a_visited += 1;
        }

        public void visit_b(B b)
        {
            Console.WriteLine(b.do_b());
            this.num_b_visited += 1;
        }

    }

    class Program
    {
        static void Main(string[] args)
        {

            Worker worker = new Worker();

            IActionVisitor<Letter> visitor = Visitor.For<Letter>(worker.visit_unknown);
            visitor.Register<A>(worker.visit_a);
            visitor.Register<B>(worker.visit_b);
            visitor.Register<Letter>(worker.visit_letter);

            Letter letter = new Letter();
            A a = new A();
            B b = new B();
            C c = new C();

            Letter[] letters = new Letter[] { letter, a, b, c };

            Console.WriteLine("Calling Visit on Concrete variable types");
            visitor.Visit(letter);
            visitor.Visit(a);
            visitor.Visit(b);
            visitor.Visit(c);
            Console.WriteLine();

            Console.WriteLine("Calling Visit on Abstract/Superclass variable types");
            foreach (Letter l in letters)
            {
                visitor.Visit(l);

            }
            Console.WriteLine();
            Console.WriteLine(String.Format("Letter's visited: {0}", worker.num_letter_visited));
            Console.WriteLine(String.Format("A's visited: {0}", worker.num_a_visited));
            Console.WriteLine(String.Format("B's visited: {0}", worker.num_b_visited));
            Console.WriteLine(String.Format("Unknown's visited: {0}", worker.num_unknown_visited));
        }
    }
}
marshcode commented 11 years ago

Multiple Dispatch in C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace multiple_dispatch
{

    public class A
    {
    }

    public class B : A
    {
    }

    public class C : B
    {
    }

    public class D
    {
    }

    public class E : D
    {
    }

    public class Visitor
    {
        public void Multi(D d, A a) { System.Console.WriteLine("DA"); }
        public void Multi(D d, B b) { System.Console.WriteLine("DB"); }
        public void Multi(D d, C c) { System.Console.WriteLine("DC"); }

    }

    public class SubVisitor1 : Visitor
    {
        public void Multi(E e, A a) { System.Console.WriteLine("EA"); }
        public void Multi(E e, B b) { System.Console.WriteLine("EB"); }
        public void Multi(E e, C c) { System.Console.WriteLine("EC"); }
    }
    public class SubVisitor2 : Visitor
    {
        public void Multi(E e, A a) { System.Console.WriteLine("EA"); }
        public void Multi(E e, B b) { System.Console.WriteLine("EB"); }
    }

    class Program
    {

        static void do_work(Visitor test)
        {
            A a = new A();
            A b = new B();
            A c = new C();
            D d = new D();
            D e = new E();
            Console.WriteLine("Single Dispatch");
            test.Multi(d, a);
            test.Multi(e, b);
            test.Multi(e, c);
            Console.WriteLine();
            Console.WriteLine("Multiple Dispatch via dynamic keyword.");
            test.Multi((dynamic)d, (dynamic)a);
            test.Multi((dynamic)e, (dynamic)b);
            test.Multi((dynamic)e, (dynamic)c);
            Console.WriteLine();
            Console.WriteLine("Multiple Dispatch via dynamic keyword in call AND assignment.");
            var t = (dynamic)test;
            t.Multi((dynamic)d, (dynamic)a);
            t.Multi((dynamic)e, (dynamic)b);
            t.Multi((dynamic)e, (dynamic)c);
        }

        static void Main(string[] args)
        {

            Console.WriteLine("=======SubVisitor1==========");
            Visitor subvisitor1 = new SubVisitor1();
            Program.do_work(subvisitor1);
            Console.WriteLine("=======SubVisitor2==========");
            Visitor subvisitor2 = new SubVisitor2();
            Program.do_work(subvisitor2);

        }
    }
}
marshcode commented 11 years ago

Consider the other attached file as a different mechanism. This uses built in C# items (instead of doing the dispatching ourselves). It involves the use of the dynamic keyword like the first one but this one utilizes var.

Given that we pass in a Visitor but it is used like a SubVisitor?, it looks like this is another form of down casting.