Wizards and warriors, part two

In this series we’re exploring the problem “a player can use a weapon, a wizard is a kind of player, a staff is a kind of weapon, but a wizard can only use a staff”. The best solution we’ve come up with so far is to throw a conversion violation at runtime if the developer makes a mistake, which seems less than optimal.

Attempt #3

abstract class Player 
{
  public Weapon Weapon { get; set; }
}

sealed class Wizard : Player
{
  public new Staff Weapon 
  {
    get { return (Staff)base.Weapon; }
    set { base.Weapon = value; }
  }
}

This has the nice property that if you have a Wizard in hand, the public surface area now gives you a Staff when you fetch the Weapon, and the type system prevents you from assigning a Sword to the Weapon of the Wizard.

But it has some not-so-nice properties. We’re still in violation of the Liskov Substitution Principle: if we have a Player in hand then we can assign a Sword to its Weapon without failure:

Wizard wizard = new Wizard();
Player player = wizard;
player.Weapon = new Sword(); // Fine
Staff staff = wizard.Weapon; // Boom!

In this version the exception happens when we try to take the Wizard‘s staff! This sort of “time bomb” exception is really hard to debug, and it violates the guideline that a getter should never throw.

We could fix that problem like this:

abstract class Player 
{
  public Weapon Weapon { get; protected set; }
}

Now if you want to set the weapon you need to have a Wizard in hand, not a Player, and the setter enforces type safety.

This is pretty good, but still not great; if we have a Wizard and a Staff in hand but the Wizard is in a variable of type Player then we need to know what to cast the Player to in order to do what is a legal property set, but is not allowed on Player. And of course the same problem exists if the Staff is in a variable of type Weapon; now we have to know what to cast it to in order to do the property set. We have not actually gained much here; there are still going to be conversions all over the show, some of which may fail, and then the failure case has to be considered.

Attempt #4

Interfaces! Yeah, that’s the ticket. Every problem with class hierarchies can be solved by adding more abstraction layers, right? Except maybe the problem “I have too many abstraction layers.”

interface IPlayer 
{
  Weapon Weapon { get; set; }
}

sealed class Wizard : IPlayer
{
  Weapon IPlayer.Weapon 
  {
    get { return this.Weapon; }
    set { this.Weapon = (Staff) value; }
  }
  public Staff Weapon { get; set; }
}

We’ve lost the nice property that we have a convenient container for code common to all players, but that’s fixable by making a base class.

Though this is a popular solution, really we’re just pushing the problem around rather than fixing it. Polymorphism is still totally broken, because someone can have an IPlayer in hand, assign a Sword to Weapon, and that throws. Interfaces as we’ve used them here are essentially no better than abstract base classes.

Attempt #5

It’s time to get out the big guns. Generic constraints! Yeah baby!

abstract class Player<TWeapon>  where TWeapon : Weapon
{
  TWeapon Weapon { get; set; }
}
sealed class Wizard : Player<Staff> { }
sealed class Warrior : Player<Sword> { }

This is so awful I don’t even know where to even begin, but I’ll try.

First off, by adding parametric polymorphism we have lost subtype polymorphism completely. A method can no longer take a player; it must take a-player-that-can-use-a-particular-weapon, or it must be a generic method.

We could solve this problem by making the generic player type inherit from a non-generic player type that doesn’t have a Weapon property, but now you cannot take advantage of the fact that players have weapons if you have a non-generic player in hand.

Second, this is a thorough abuse of our intuition about generic types. We want subtyping to represent the relationship “a wizard is a kind of player” and we want generics to represent the relationships “a box of fruit and a box of biscuits are both kinds of boxes, but not kinds of each other”. I know that a fruit basket is a basket of fruit, but I see “a wizard is a player of staffs” and I do not know what that means. I have no intuition that the generics as we have used them here mean “a wizard is a player that is restricted to using a staff.”

Third, this seems like maybe it will not scale well. If I want to also say “a player can wear armor, but a wizard can only wear robes”, and “a player can read books, but a warrior can only read non-magical books”, and so on, are we going to end up with a half-dozen type arguments to Player?

Attempt #6

Let’s combine attempts 4 and 5. The problem arises when we try to mutate the weapon. We could make the player classes immutable, construct a new player every time the weapon changes, and now we can make the interface covariant. Though I am a big fan of immutable programming, I don’t much like this solution. It seems like we are trying to model something that is really mutable in code, and so I like having that mutation represented in the code. Also, it’s not 100% clear how this solves the problem; if we have a player in hand that we are cloning then we still need some way to prevent a new wizard from being created with a sword.

Oh, yeah, one other thing. Seeing Aragorn with his arsenal there reminded me, did I mention earlier that wizards and warriors can both use daggers?

Maybe your users always tell you the exact business domain constraints perfectly accurately before you start coding every project and they never change in the future. Well, I’m not that kind of user. I need the system to handle the fact that wizards and warriors can both use daggers, and no, daggers are not “a special kind of sword”, so don’t even ask.

Attempt #7

Plainly what we need to do here is combine all the techniques we’ve seen so far in this episode:

sealed class Wizard : 
  IPlayer<Staff, Robes> , 
  IPlayer<Dagger, Robes>
{ ...

I can’t bear to finish. I think just writing that little bit took an extra few minutes off my life.

Next time on FAIC: We’ll leave the staves-and-swords problem aside for the moment and consider some related problem in class hierarchy design: suppose a paladin swings a sword at a werewolf in a church after midnight. Is that a concern of the player, monster, weapon or location classes? Or maybe some combination of them? And if the types of none of them are known at compile time, what then?

Advertisements

45 thoughts on “Wizards and warriors, part two

  1. The other problem with using an interface is that is breaks the basic idea that a base class indicates that a Wizard “is a” Player, and instead indicates that as Wizard “has a” Player.

    When I had my first OOP class in the mid 90’s, the “is a”/”has a” distinction was drilled into us when it comes to base classes vs interfaces.

    • I’ve never head of that distinction wrt base classes and interfaces, and it doesn’t seem to hold up from the perspective of the dependent.

      if I were to create:
      void StartGame(IPlayer player)

      I would expect you to give me something that “is a” player.
      It’s not important to me whether that interface has been implemented directly, or by delegating to a “has a” relationship.

    • I think of a derived class relationship being an “is a kind of” relationship, but I have never heard of an interface as being a “has a” relationship. I think of an interface as a “I can behave like” or “I provide the service of” relationship. An IEnumerable does not *have* a sequence. It is something that can behave like a sequence.

      • I prefer to think of interfaces as being “is a thing that’s usable as a ___” or “is a thing that can ___”. While it’s true that implementation of an interface indicates that an object “has” the abilities implied by the interface, any a type which implements a derived interface also “has” the abilities implied by the base, I would consider much more fundamental the fact that an object which implements the derived interface *is* an object which implements the base interface.

  2. Why don’t you try to use validation callbacks. You add these validation callbacks when the Wizard is created. Then ensure they are being called when you try to assign a Sword to the Wizard when it is accessed as Player

      • When a weapon is equipped, a callback should be informed of the effective stats for base hit percentage, enemy armor-class scaling, attack strength, etc. that would be applied to that combination of player and weapon. If the caller ignores the fact that its base-hit percentage is so bad that it would have a 99% chance of missing a sleeping enemy and a 100% chance of missing anything else and tries to attack anyway, the aforementioned stats should be applied. If the enemy is asleep and remains asleep despite dozens of attempted attacks, the player might actually score a hit. If the enemy isn’t asleep, any attempts to attack it would miss, just as would happen if a low-level warrior with a basic sword were to attack a very spry imp whose whose speed made it safe from any sword without at least a +4 modifier. In either case, the player character should decide after consistently failing to score a hit that perhaps some other means of attack was required.

        The only real problem situation would be if code for the player character decided to keep attacking a monster until either it or the player was defeated, and a monster was encountered which was effectively invulnerable to the player but which was also incapable of damaging the player. That situation could be particularly acute with a poorly-wielded weapon, but would really be no different from a player trying to attack an enemy whose defenses were far beyond his ability to attack, but which otherwise considered the player to be unworthy of notice and not worth attacking. Such issues could likely be resolved by having each failed attack cost an amount like 0.000000001hp, but have the amount increase throughout the combat until someone wins.

        • Well, this is all very nice for the wizards and warriors scenario, but it’s not clear how it applies to the papers and paychecks scenario. If we assign the mailboy as the expense report approver of the senior manager, in violation of policy, then what happens? We keep on throwing expense reports at the mailboy until the expense report is defeated? 🙂

          • For many kinds of applications, failures are possible at almost any stage of a process that needs to interact with the outside world. Even if a program confirms that everything’s all set for the paychecks to go through, there’s always the possibility of something going wrong. Setting the mailboy as the expense-report approver may yield a response indicating that the payment request is apt to get rejected, but that fact doesn’t mean the request is necessarily invalid. If it’s presently 11:59pm, a policy change is going to become effective at 12:01am that would authorize the mailboy as an expense approver, and requests must be submitted before 12:00am to be processed in the batch which will be validated at 12:02am, it may be perfectly right and proper to submit an expense report which is contrary to policy at the time it is written but will be consistent with policy by the time it is processed.

  3. Your last attempt doesn’t go far enough. Clearly what you want is:

    sealed class Wizard : 
      IPlayer, 
      ICanUseWeapon<Staff>, 
      ICanUseWeapon<Dagger>, 
      ICanWear<Robes>
    

    (Hmm, I started this as a joke, and now that I think about it, there might be cases where this is the best solution.)

    • It’s not terrible, but it still has problems. We have an IPlayer that happens to be a Wizard and a Weapon that happens to be a staff. How do we know to interrogate the IPlayer to ask it for ICanUseWeapon<Staff>?

    • var wizard = new Wizard()
      ((ICanUseWeapon<Staff>)wizard).Weapon = new Staff();
      Dagger weapon = ((ICanUseWeapon<Dagger>)wizard).Weapon; // Boom
      

      Its exactly same issue.

    • keep it simple(r):

      interface IEquipStaff { void Equip(Staff staff); }

      sealed class Wizard: …,…, IEquipStaff

  4. “if … the Wizard is in a variable of type Player … [or] … the Staff is in a variable of type Weapon”
    My inclination is to say that we shouldn’t be assigning arbitrary weapons or to arbitrary players knowing that there are restrictions. If I don’t know that I have a Wizard, I shouldn’t be trying to assign a Staff to him. It’s not unlike passing null to a function; that’s boneheaded exception territory, because you can easily encode a check for validity of the assignment before performing the bogus assignment. If we insist on storing the “can use” rules in the player classes, I’d probably have Player include an abstract Method like “CanUse(Weapon weaponToUse)”. Wizard would override with “return weaponToUse is Staff”.
    If all of this runtime type stuff is undesirable, we’ve just hit more reasons to use an alternative method of differentiating/resolving classes and weapon types! Instead of looking at type, maybe each Weapon encodes its in-game format somehow (enum?), and each Player would determine usability by that property.

    Looking into the next topic, I’d probably vote to move most interaction logic into some sort of external battle engine. Maybe use that ECS people suggested on the last entry. If not, I’d look at which classes “own” the rules; Swords know “base” damage, Paladins know how to modify that based on special target/location bonuses, werewolfs know how to resist it, etc. Regarding types not being known: we’re probably calling a method in Player, since the player initiated the action. The method should be overwritten in Paladin, which suddenly gives us that information. Paladin eventually calls sword.UseAgainst(target), which is overridden with Sword-specific data, which ends up calling target.Defend(baseSwingDamage), where Werewolf’s Defend method reacts appropriately to the damage. If we can’t virtualize actions like this, then what good is a base class like Weapon?

    We should really get away from inheritance if we can’t abstract properly. At that point we’re just using the Weapon base class as one of many properties, which can just as easily be represented by a Property!

  5. Maybe I’ve just been lucky, but the projects I’ve worked on never seem to run into these sorts of problems. Games, on the other hand, seem to generate them in spades. I think it comes down to game rules containing so many arbitrary restrictions and exceptions. A business, after all, would never say “I’m sorry sir, but I can’t sell you this sword because you’re a wizard.”

    I’m really curious what you’ll present in this series. It’s a problem I’ve struggled with for many years, and I’ve never come up with a completely satisfactory solution. Modelling the game, after all, is only half the problem. We also want our design to be extensible so we can easily add new (and arbitrary) rules in the future.

    • Simple, you forget about having those constraints in type system. I believe if there is no “behavior” to either weapons or players, then there is no need for classes and inheritance.

  6. As a game developer, I can attest that this entire series is solving the problem in exactly the wrong way. It’s fundamentally mis-stating the premise of the problem. A Wizard *is not* a player. Rather, the player *has a* character class of type Wizard.

    Now in your Weapon property setter, you can simply say:
    if(characterClass.IsValid(value)) _weapon = value;

    Or, more generally:
    if(IsValidWeapon(value)) _weapon = value;

    And then you can ask more general questions related not just to character class, but also to inventory space, encumbrance, handedness, etc. (After all, if your character is right handed, she can’t equip the Left-Handed Scissors!)

    I would apply a similar distinction to the weapons. Rather than a Staff or a Sword or a Dagger being its own data type, I would just have a Weapon, and the data of the weapon describes its behavior and its restrictions and requirements.

    Just my farthing’s worth.

    • Right, this is the “entity component system” pattern that is common in game development, and was mentioned in comments in the first episode.

      Though I’m not going to go into ECS proper in this series — spoiler alert — I am going to come to the same conclusion that you led with; this is a mess. Maybe it’s reasonable to have a Wizard class and maybe it isn’t, but regardless, the rules expressible in the C# type system and the rules needed in the business domain are a poor fit for each other.

      Regardless of whether we encode the relationship between Player and Wizard in the type system, we still have the problem of “where does the code go?” when trying to resolve interactions of many different types of things. In the next couple of episodes we’ll look at that problem.

  7. [I know you’re a fan of Inform 7, a language designed for just this type of problem involving conflicting rules and special cases. I don’t think this will provide much insight for the C# question, since the Inform Way is usually to avoid putting this information in the type system at all, but it’s somewhat like the ECS pattern that keeps coming up — not surprising, since ECS is mostly used in games.]

    Section 1 – Definitions

    A wizard is a kind of person.
    A warrior is a kind of person.

    A weapon is a kind of thing.
    A dagger is a kind of weapon.
    A sword is a kind of weapon.
    A staff is a kind of weapon.

    Wielding is a thing based rulebook. The wielding rules have outcomes allow it (success), it is too heavy (failure), it is too magical (failure).
    The wielder is a person that varies.

    To consult the rulebook for (C – a person) wielding (W – a weapon):
    now the wielder is C;
    follow the wielding rules for W.

    Wielding a sword: if the wielder is not a warrior, it is too heavy.
    Wielding a staff: if the wielder is not a wizard, it is too magical.
    Wielding a dagger: allow it.

    Section 2 – Example

    Dungeon is a room.
    Gandalf is a wizard in Dungeon.
    Conan is a warrior in Dungeon.

    The rusty dagger is a dagger in Dungeon.
    The elvish sword is a sword in Dungeon.
    The oaken staff is a staff in Dungeon.

    Instead of giving a weapon (called W) to someone (called C):
    consult the rulebook for C wielding W;
    if the rule failed:
    let the outcome text be “[outcome of the rulebook]” in sentence case;
    say “[C] declines. ‘[outcome text].'”;
    otherwise:
    now C carries W;
    say “[C] gladly accepts [the W].”

    The can’t take people’s possessions rule is not listed in any rulebook.

    Test me with “give sword to gandalf / give sword to conan / give staff to conan / give staff to gandalf / give dagger to gandalf / get dagger / give dagger to conan”.

  8. It looks like your game’s rules are not a good fit for static typing. You could describe the game rules in a dynamic language or subsystem. There are other benefits: it enables changing the rules at runtime, for gameplay exploration, balancing, debugging, or for extending the game. It is common practice to have the game be separated into an engine implemented in a statically-typed language, and the rules in a dynamically-typed language.
    But of course all I said is irrelevant because you only chose wizards & warriors as an example domain. In real code when that kind of thing happen, I am usually ok with exceptions being thrown, but you seem to have something else in mind.

  9. The issue is that the Wizard or Warrior HAS a weapon, which maps very neatly with property get semantics. However, the process of “equipping” said weapon, does not map well at all with property set semantics. Exposing the Weapon (as type Weapon) through a getter (abstract in the base Player class) as well as an abstract Equip(Weapon) method is a closer semantic match to the business* logic.

    *Wiz-Biz, that is

  10. Can we associate weapons with the players that can wield them?

    abstract class Player
    {
    public IWeapon Weapon { get; protected set; }
    }

    sealed class Wizard : Player
    {
    public void SetWeapon(IWeapon weapon)
    {
    Weapon = weapon;
    }
    }

    sealed class Warrior : Player
    {
    public void SetWeapon(IWeapon weapon)
    {
    Weapon = weapon;
    }
    }

    interface IWeapon { }
    interface IWeapon : IWeapon where T : Player { }

    sealed class Staff : IWeapon { }
    sealed class Sword : IWeapon { }
    sealed class Dagger : IWeapon, IWeapon { }

    • Sorry, my generic types were formatted out.

      abstract class Player
      {
          public IWeapon Weapon { get; protected set; }
      }
      
      sealed class Wizard : Player
      {
          public void SetWeapon(IWeapon<Wizard> weapon)
          {
              Weapon = weapon;
          }
      }
      
      sealed class Warrior : Player
      {
          public void SetWeapon(IWeapon<Warrior> weapon)
          {
              Weapon = weapon;
          }
      }
      
      interface IWeapon { }
      interface IWeapon<T> : IWeapon where T : Player { }
      
      sealed class Staff : IWeapon<Wizard> { }
      sealed class Sword : IWeapon<Warrior> { }
      sealed class Dagger : IWeapon<Wizard>, IWeapon<Warrior> { }
      
      • The problem is that it will become a nightmare as you get more and more different character classes.

        public class Dagger : 
          IWeapon<Wizard>, 
          IWeapon<Warrior>, 
          IWeapon<Rogue>, 
          IWeapon<Jedi>, 
          IWeapon<Republican>
        

        That and a weapon shouldn’t have any idea about what can use it and what can’t.

        Oh and new classes won’t be able to use existing weapons.

  11. Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1852

  12. Pingback: Dew Drop – May 1, 2015 (#2005) | Morning Dew

  13. Simple, just let the weapon be get / set as an IWeapon. If they try to set a bad weapon (such as a sword for a wizard), then silently fail and instead set the weapon to an Unarmed weapon. /sarcasm

    • If the semantics of “wield” are “prepare to use the item as a weapon as effectively as possible” and the semantics are “attack” are “perform with the wielded item whatever action one would use to damage a monster”, then any character could wield any item they could carry. For the warrior, attacking with the staff would mean swinging it like a stick, at least until it breaks. For the wizard, casting a spell with a sword would mean pointing it at the enemy, uttering some incantations, and feigning surprise when nothing happens. The only basis I would see for unwielding the weapon would be if the player was unable to hold it and consequently dropped it–an event which could happen asynchronously in some cases even with a player holding a “proper” weapon (e.g. if the player was hit by a “fumble-fingers” spell.

  14. After reading this, I now see why interfaces won’t work… this is a bit more tricky than I had thought! For some reason, I was thinking that the following would work (and wasn’t even using interfaces correctly for this use case):

    public abstract class PlayerCharacter
    {
         private IEquippable _equipment;
    }
    
    public class Knight : PlayerCharacter
    {
         public Sword Equipment
         {
              get
              {
                   return _equipment as Sword;
               }
               set
               {
                    _equipment = value;
               }
           }
    }
    

    This obviously isn’t really great at all, as anybody who would want to maintain that could have absolutely no idea that there is even an _equipment field on the abstract PlayerCharacter class in the first place! D’oh!

  15. I see where this is going, your are going to show how many times people can write a wrong class hierarchy, but there is something flawed in this series: the assumption that warriors and paychecks are the same thing so solutions that apply to one will apply to other, well… They aren’t the same thing… And solution aren’t the same because nobody is going to rewrite the entire world, rather, just the minimum needed to fulfill the job, experienced professionals will write a bit more, the extra bit they predict to be needed in the future, but this last piece is area specific, without experience in that specific area there is no way for the programmer to predict what will be needed.

    By the end of the first part if someone wrote a Player class with an abstract Weapon property and Wizard and Warrior classes that override and throws when receiving an invalid weapon that code would be perfectly fine, how could them predict you would be adding Dagger and Armor with that exception for Wizards? Well… RPGs have a lot of Weapons, but let’s say that instead of “Weapon” property it was a “Sex” property? Usually RPGs only have two sexes (I didn’t know this word exists, the grammar checker told me), would any of the proposed solutions for the Weapon be suitable for Sex? This is very specific to this area.

    I have near zero experience in coding games, but have some experience with paychecks and audits, and no code looks like anything proposed here, but, on business I never try to create a virtual world with it’s own mechanics, in the case of games I guess the differences between business rules and the mechanics are much more blurry.

    About the problem of throwing an exception when a Warrior tries to equip a Staff, well, it may happens, and must be handled somehow, maybe a “CanUse” method so the game interface can ttell the user he can’t use that or maybe throwing exceptions, a “CanUse” methods is more usefull for the game interface to list weapons the player can actually use.

    About the Paladin slashing the Werewolf, I suppose time and location are important because they change the characters behaviors, the responsibility should be split between the Paladin and the Werewolf, the Paladin calculates it’s attack power based on the equipped weapon. location, time and opponent and then Werewolf take the resulting damage object and take location, time, armor and opponent to calculate how much life it will loss.

    All that said when thinking about the problem I started writing thinks in an interesting pattern, a bit WET because every equipment have to contain a “public bool CanUse(Player player) { return player.CanUse(this); }” method, still, little overhead for visiting, hope people can give ideas on how to improve it, sorry for the lack of indentation, I am trying to not take too much space:

        abstract class Player : ICanUse<IArmor>
        {
            private IWeapon _weapon;
            private IArmor _armor;
            public virtual bool CanUse<T>(T equipment)
                where T:IEquipment
            {
                ICanUse<T> temp = this as ICanUse<T>;
                return temp != null && temp.CanUse;
            }
            private T Check<T>(T equipment)
                where T:IEquipment
            {
                if (equipment.CanUse(this))
                    return equipment;
                else
                    throw new InvalidOperationException("I don't know how to use this");
            }
            public virtual IWeapon Weapon
            {
                get { return _weapon; }
                set { _weapon = Check(value); }
            }
            public virtual IArmor Armor
            {
                get { return _armor; }
                set { _armor = Check(value); }
            }
            bool ICanUse<IArmor>.CanUse { get { return true; } }
        }
        interface IEquipment { bool CanUse(Player player); }
        interface IWeapon: IEquipment { }
        interface IArmor: IEquipment { }
        class Wizard : Player, ICanUse<Staff>, ICanUse<Dagger>, ICanUse<IArmor>, ICanUse<Robe>
        {
            bool ICanUse<IArmor>.CanUse { get { return false; } }
            public bool CanUse { get { return true; } }
        }
        class Warrior : Player, ICanUse<Sword>, ICanUse<Dagger> { public bool CanUse { get { return true; } } }
        class Sword : IWeapon { public bool CanUse(Player player) { return player.CanUse(this); } }
        class Staff : IWeapon { public bool CanUse(Player player) { return player.CanUse(this); } }
        class Dagger : IWeapon { public bool CanUse(Player player) { return player.CanUse(this); } }
        class Robe : IArmor { public bool CanUse(Player player) { return player.CanUse(this); } }
        class Scale : IArmor { public bool CanUse(Player player) { return player.CanUse(this); } }
        interface ICanUse<in T> where T : IEquipment { bool CanUse { get; } }
    
  16. It seems to me that the ultimate goal is to get the compiler to output an error when we violate business logic based on custom polymorphic types. Since we want to use base classes, Player and Weapon, it’s always going to be possible that we end up in a situation where we’ve got a Player reference and a Weapon reference and we want to assign the Weapon to the Player, but we don’t know their types statically. There’s no way for the compiler to help us here, right? So maybe it’s just a bad assumption to begin with.

    In order to use polymorphism at all, we should probably relax our constraints and allow any Weapon to be assigned to any Player, and then allow derived classes to apply constraints, perhaps including a Try* method to avoid exceptions in circumstances where they’re expected.

    abstract class Player
    {
      public virtual Weapon Weapon { get; set; }
      public abstract bool TrySetWeapon(Weapon weapon);
    }
    

    At least with a virtual property we can get a runtime exception *immediately upon assignment*, not at some later time that’s hard to debug, even though it’s not as ideal as static checking.

    It kind of makes sense against a real-life analogy anyway; e.g., I can try to hand a Wizard a ScaryNewWeapon, and he can simply take it and not use it. There’s no “universal” constraint that prevents me from attempting to give a ScaryNewWeapon to a Wizard, the entire universe halting if I do. Perhaps the Wizard should decide what happens next; e.g.,

    1. Assignment succeeds but the Wizard’s behavior is unaffected (Wizard can’t use the weapon).

    2. Assignment is simply ignored (Wizard doesn’t accept ScaryNewWeapon)

    3. Othewrise, the universe halts.

    If we wish to choose only one of the aforementioned behaviors for all derived types, then we could codify the casting logic externally and in one place, although perhaps it’s still not ideal:

    abstract class Player
    {
      public Weapon Weapon { get; protected set; }
    }
    
    class Wizard : Player, IUseWeapon
    {
      public void SetWeapon(Staff staff) { ... }
    }
    
    static class PlayerWeapons
    {
      public static bool TrySetWeapon(this Player player, Staff weapon)
      {
        var strategy = player as IUseWeapon;
    
        if (strategy != null)
        {
          strategy.SetWeapon(weapon);
          return true;
        }
        else // behavior #2 above
          return false;
      }
    
      public static bool TrySetWeapon(this IUseWeapon holder, Weapon weapon)
      {
        var staff = weapon as Staff;
    
        if (staff != null)
        {
          holder.SetWeapon(staff);
          return true;
        }
        else // behavior #2 above
          return false;
      }
    }
    
  17. Love the Wizards and Warriors example! I’m looking forward to the rest of this series. Have definitely come across this modelling issue many times, as I’m sure every other OOP developer has too.

  18. No way to provide public setter of Weapon property in Player class if you want to have static type checks. To avoid hell of interfaces I might use such code:

    public class Weapon { }
    public class Sword : Weapon { }
    public class Staff : Weapon { }
    public abstract class Player
    {
    	public virtual Weapon Weapon { get { return GetPlayerWeapon(); } }
    	protected abstract Weapon GetPlayerWeapon ();
    }
    public class Warrior : Player
    {
    	protected Sword weapon;
    	public new Sword Weapon { get { return weapon; } set { weapon = value; }}
    	protected sealed override Weapon GetPlayerWeapon ()
    	{
    		return weapon;
    	}
    }
    public class Wizard : Player
    {
    	protected Staff weapon;
    	public new Staff Weapon { get { return weapon; } set { weapon = value; } }
    	protected sealed override Weapon GetPlayerWeapon ()
    	{
    		return weapon;
    	}
    }
    

    More flexible solution using interfaces

    public interface IPlayer
    {
    	Weapon Weapon { get; }
    }
    public abstract class Player : IPlayer
    {
    	Weapon IPlayer.Weapon { get { return GetWeapon(); } }
    	protected abstract Weapon GetWeapon ();
    }
    public interface ICanWearWeapon<T> where T : Weapon
    {
    	T Weapon { get; set; }
    }
    public class Warrior : Player, ICanWearWeapon<Sword>
    {
    	public Sword Weapon { get; set; }
    	protected override Weapon GetWeapon ()
    	{
    		return Weapon;
    	}
    }
    public class Wizard : Player, ICanWearWeapon<Staff>
    {
    	public Staff Weapon { get; set; }
    	protected override sealed Weapon GetWeapon ()
    	{
    		return Weapon;
    	}
    }
    public static class Extensions
    {
    	public static void ModifyWarriorWeapon<T> (this T instance) where T : IPlayer, ICanWearWeapon<Sword>
    	{
    		var equipment = instance as ICanWearWeapon<Sword>;
    		equipment.Weapon = new Sword();
    		// maybe use some other properties of IPlayer for calculations
    	}
    }
    
  19. Pingback: Basic OOP is easy, isn’t it? | gerleim

  20. Ok, I think I am getting a headache now!
    I wonder if we can do something else. So let’s forget about who can equip what and we take this responsibility out of the player and weapon class altogether ?

    How about we build an external rules engine? Think of something like Automapper, before you can cast anything to anything you need to define that as a relationship.

    So our player base class is still generic, it still has a property called IWeapon.
    Then we have an EquipWeapon method on this class which takes an instance of IWeapon.
    At this point we check with our rules engine to see if that type of weapon can be equipped by that player entity. The output is a bool, so we return false if that item cannot be equipped by that particular player type and true if it can.

    Yes it still means we need to maintain of list of who mcan use what, but we need to do that somehow anyway. The rules could be in a file, loaded at runtime so we could change them whenever we have something new to add.

    This way there is no exception thrown, we get a bool we can work with and decide how to show that the particular weapon can not be used by that player archetype.

    so here is what I envision :

    public abstract class PlayerBase
    {
    public IWeapon playerWeapon;
    public RulesEngine rulesEngine = new RulesEngine();

    public virtual bool EquipWeapon(IWeapon weapon)
    {
    if (rulesEngine.CheckRule(this, weapon))
    {
    this.playerWeapon = weapon;
    return true;
    }

    return false;
    }
    }

    public interface IWeapon
    {
    }

    public sealed class Staff : IWeapon
    {
    }

    public sealed class Wizard : PlayerBase
    {
    }

    public sealed class RulesEngine
    {
    public readonly Dictionary RulesEngineDict = new Dictionary();

    public RulesEngine()
    {
    this.RulesEngineDict.Add(typeof(Wizard), typeof(Staff));
    }

    public bool CheckRule( PlayerBase player, IWeapon weapon )
    {
    //at this point player.GetType() returns wizard
    //weapon.Gettype() returns staff and we check we have this combo in our dictionary.
    if we do, return true, if not return false
    return false;
    }

  21. Pingback: Wizards and warriors, part five | Fabulous adventures in coding

  22. I understand that instead of wizards\warriors rpg any other domain could be used as an example. But in my opinion this concrete domain is not a best example and is misleading.
    Imho, in this concrete scenario we don’t need any compile-time type checks or exceptions (_trying_ to equip sword with wizard is a normal flow). Enforcing weapons a wizard can equip in such way, is like trying to enforce an invariant “Person name length should be shorter than 255 characters” with “ShortName” and “LongName” classes.
    Most real-life rpgs would probably give weapon collection of “something” indicating what character-class can equip it (or can’t, or even some complex predicates for cases when ability to equip weapon depends on some character state). And for those who say “Knowing about who can equip it is not a concern of weapon”, treat this “something” like a text written on a sword hilt. At a players request “please equip this”, wizard reads this text on a sword hilt and makes his decision. And he can even notify player back either with “Equip”‘s method return value or, for example, events.

    A domain where we want such cheks would be something like “MysqlCommand can’t equip OracleConnection”.

  23. Is that okay?

    class Weapon { }
    class Sword : Weapon { }
    class Staff : Weapon { }
    class Dagger : Weapon { }
    abstract class Player
    {
    public virtual Weapon Weapon
    {
    get { return weapon; }
    set
    {
    if (CanEquip(value))
    weapon = value;
    }
    }
    private Weapon weapon;
    protected virtual bool CanEquip(Weapon weapon)
    {
    return true;
    }
    }
    sealed class Wizard : Player
    {
    protected override bool CanEquip(Weapon weapon)
    {
    return !(weapon is Sword);
    }
    public Staff Staff // quick access to class’s favorite weapon
    {
    get
    {
    return Weapon as Staff;
    }
    set { Weapon = value; }
    }
    }
    sealed class Warrior : Player
    {
    protected override bool CanEquip(Weapon weapon)
    {
    return !(weapon is Staff);
    }
    public Sword Sword // quick access to class’s favorite weapon
    {
    get
    {
    return Weapon as Sword;
    }
    set { Weapon = value; }
    }
    }

  24. Pingback: Wizards and warriors, part one | Fabulous adventures in coding

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s