• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 278
  • Last Modified:

Virtual Base Class 101 -- Teach me how

Please take a close look at the class defined below.

I had somebody mention to me in passing that I should convert the class below into a virtual base class and inherit from it for the individual game pieces, like Destroyer vs Engineer vs Medic, etc.   since all have things in COMMON (like health) and things that are special to those pieces (like only Engineers can move walls, and Master Robots are stationary objects)

Notice I have defined WALLS and EMPTY spaces which completely different from the other pieces.


Please help.





using System;
using System.Drawing;

namespace Robotz
{      
      public class GamePiece
      {
            public const int MasterRobot = 1;
            public const int Medic = 2;
            public const int Engineer = 4;
            public const int Destroyer = 8;
            public const int Wall = 256;
            public const int Empty = 512;

            public string owner="NONE ";
            public string piece_name;
            public string piece_description;
            public int health;
            public int attack_strength;
            public string image_filename;
            public int use_image_index;
            public bool can_drag=false;
            

            public bool repaint_pending = false;

            //1 = MR
            //2 = MED
            //4 = ENG
            //8 = DEST
            //256 = WALL
            //512 = EMPTY

            public int piece_type;

            public GamePiece()
            {
            }

            public string DisplayImage()
            {
                  return image_filename;
            }

            public void AssignAsNewMasterRobot(string temp_owner,bool is_black)
            {
                  owner = temp_owner;
                  piece_name = "Master Robot";
                  piece_description = "Guard this piece at all costs!  Once this piece is killed you loose the game.";
                  piece_type = MasterRobot;
                  attack_strength = 0;
                  health = 200;
                  can_drag=false;
                  if(is_black)
                  {
                        use_image_index= (int)GamePieceGraphicIndex.MasterRobot_Black;
                  }
                  else
                  {
                        use_image_index= (int)GamePieceGraphicIndex.MasterRobot_White;
                  }
            }

            public void AssignAsNewMedic(string temp_owner,bool is_black)
            {
                  owner = temp_owner;
                  piece_name = "Medic";
                  piece_description = "Able to heal other pieces that you control.  True to its oath to do no harm, it cannot attack or defend itself.  Not that you would  WANT  to...but Medics cannot heal enemy pieces.";
                  piece_type = Medic;
                  attack_strength = 20;
                  health = 100;
                  can_drag=true;
                  if(is_black==true)
                  {
                        use_image_index= (int)GamePieceGraphicIndex.Medic_Black;
                  }
                  else
                  {
                        use_image_index= (int)GamePieceGraphicIndex.Medic_White;
                  }
            }

            public void AssignAsNewEngineer(string temp_owner,bool is_black)
            {
                  owner = temp_owner;
                  piece_name = "Engineer";
                  piece_description = "Can push walls to new locations.  If you push a wall to the edge of the board it will be stuck on that edge, so be careful.";
                  piece_type = Engineer;
                  attack_strength = 10;
                  health = 100;
                  can_drag=true;
                  if(is_black)
                  {
                        use_image_index= (int)GamePieceGraphicIndex.Engineer_Black;
                  }
                  else
                  {
                        use_image_index= (int)GamePieceGraphicIndex.Engineer_White;
                  }
            }

            public void AssignAsNewDestroyer(string temp_owner,bool is_black)
            {
                  owner = temp_owner;
                  piece_name = "Destroyer";
                  piece_description = "Built for attacking other pieces.  You don't want to go toe to toe with Destroyers for very long...they will wipe you out.";
                  piece_type = Engineer;
                  attack_strength = 20;
                  health = 100;
                  can_drag=true;
                  if(is_black)
                  {
                        use_image_index= (int)GamePieceGraphicIndex.Destroyer_Black;
                  }
                  else
                  {
                        use_image_index= (int)GamePieceGraphicIndex.Destroyer_White;
                  }
            }

            public void AssignAsNewWall()
            {                  
                  owner="NONE ";
                  piece_type = Wall;            
                  piece_name = "Wall";
                  attack_strength = 0;
                  health = 0;
                  can_drag=false;
                  use_image_index=(int)GamePieceGraphicIndex.Wall;
                  piece_description = "All game pieces must travel around this obstacle.  However, Engineers can push walls along their direction of travel.";
            }

            public void AssignAsNewEmpty()
            {                  
                  owner="NONE ";
                  piece_type = Empty;            
                  piece_name = "Empty Square";
                  attack_strength = 0;
                  health = 0;
                  can_drag=false;
                  use_image_index=(int)GamePieceGraphicIndex.MoveableSpace;
                  piece_description = "Medics, Engineers, and Destroyers can move to these empty squares.";
                  image_filename = "blank.jpg";
            }
      }
}
0
Tom Knowlton
Asked:
Tom Knowlton
3 Solutions
 
Tom KnowltonWeb developerAuthor Commented:
I think I've got it.  Tell me if this is right:


BASE CLASS:

namespace Robotz
{      
      public enum GamePieceCode
      {
            MasterRobot = 1,
            Medic = 2,
            Engineer = 4,
            Destroyer = 8,
            Wall = 256,
            Empty = 512
      }

      public class GamePiece
      {
            public string owner;
            public string piece_name;
            public string piece_description;
            public int health;
            public int attack_strength;
            public int use_image_index;
            public bool can_drag;
            
            //1 = MR
            //2 = MED
            //4 = ENG
            //8 = DEST
            //256 = WALL
            //512 = EMPTY

            public int piece_type;

            public GamePiece()
            {
            }
      
            public virtual void Create(string temp_owner,bool is_black)
            {

            }
      }
}


DERIVED CLASS:

using System;
using System.Drawing;

namespace Robotz
{      
      public enum GamePieceCode
      {
            MasterRobot = 1,
            Medic = 2,
            Engineer = 4,
            Destroyer = 8,
            Wall = 256,
            Empty = 512
      }

      public class Destroyer : GamePiece
      {
            public override void Create(string temp_owner,bool is_black)
            {
                  owner = temp_owner;
                  piece_name = "Destroyer";
                  piece_description = "Built for attacking other pieces.  You don't want to go toe to toe with Destroyers for very long...they will wipe you out.";
                  piece_type = (int)GamePieceCode.Destroyer;
                  attack_strength = 20;
                  health = 100;
                  can_drag=true;
                  if(is_black)
                  {
                        use_image_index= (int)GamePieceGraphicIndex.Destroyer_Black;
                  }
                  else
                  {
                        use_image_index= (int)GamePieceGraphicIndex.Destroyer_White;
                  }
            }
      }
}
0
 
dunglaCommented:
This is the right code, you can also place your enum to the base class, then derived class will also have the enum

BASE CLASS:

namespace Robotz
{    
     public class GamePiece
     {
         public enum GamePieceCode
         {
              MasterRobot = 1,
              Medic = 2,
              Engineer = 4,
              Destroyer = 8,
              Wall = 256,
              Empty = 512
          }
          public string owner;
          public string piece_name;
          public string piece_description;
          public int health;
          public int attack_strength;
          public int use_image_index;
          public bool can_drag;
         
          //1 = MR
          //2 = MED
          //4 = ENG
          //8 = DEST
          //256 = WALL
          //512 = EMPTY

          public int piece_type;

          public GamePiece()
          {
          }
     
          public virtual void Create(string temp_owner,bool is_black)
          {

          }
     }
}


DERIVED CLASS:

using System;
using System.Drawing;

namespace Robotz
{    
     public class Destroyer : GamePiece
     {
          public override void Create(string temp_owner,bool is_black)
          {
               owner = temp_owner;
               piece_name = "Destroyer";
               piece_description = "Built for attacking other pieces.  You don't want to go toe to toe with Destroyers for very long...they will wipe you out.";
               piece_type = (int)GamePieceCode.Destroyer;
               attack_strength = 20;
               health = 100;
               can_drag=true;
               if(is_black)
               {
                    use_image_index= (int)GamePieceGraphicIndex.Destroyer_Black;
               }
               else
               {
                    use_image_index= (int)GamePieceGraphicIndex.Destroyer_White;
               }
          }
     }
}
0
 
eternal_21Commented:
Hello knowlton!

I don't have much time to explain, but here is how I would do it:


namespace Robotz
{    
  public abstract class GamePiece
  {
    public abstract string PieceName { get; }
    public abstract string PieceDescription { get; }
  }

  public abstract class MoveablePiece: GamePiece
  {
    public abstract void Attack();
    public abstract void Move();

    protected int health, strength;

    protected MoveablePiece(int health, int strength)
    {
      this.health = health;
      this.strength = strength;
    }
  }

  public abstract class StationaryPiece: GamePiece
  {
    public abstract void Attack();

    protected int health, strength;

    protected StationaryPiece(int health, int strength)
    {
      this.health = health;
      this.strength = strength;
    }
  }
 
  public abstract class SceneryPiece: GamePiece
  {
  }

  public class MasterRobot: StationaryPiece
  {
    public MasterRobot(): base(200, 0)
    {
    }

    public override string PieceName
    {
      get { return "Master Robot"; }
    }

    public override string PieceDescription
    {
      get { return "Guard this piece at all costs!  Once this piece is killed you loose the game."; }
    }

    public override void Attack()
    {
      throw new System.NotImplementedException();
    }
  }

  public class Medic: MoveablePiece
  {
    public Medic(): base(100, 20)
    {
    }

    public override string PieceName
    {
      get { return "Medic"; }
    }

    public override string PieceDescription
    {
      get { return "Able to heal other pieces that you control.  True to its oath to do no harm, it cannot attack or defend itself.  Not that you would  WANT  to...but Medics cannot heal enemy pieces."; }
    }

    public override void Move()
    {
      throw new System.NotImplementedException();
    }

    public override void Attack()
    {
      throw new System.NotImplementedException();
    }
  }

//  public class Engineer: MoveablePiece
//  {
//    ...
//  }
//
//  public class Destroyer: MoveablePiece
//  {
//    ...
//  }
}


If you had to determine what peice was on a square for some reason, you could always do this: (I am assume that's what the integers were for?)


  object myPiece = null;

  if(myPiece is StationaryPiece)
  {
    StationaryPiece stationaryPiece = (StationaryPiece)myPiece;
    stationaryPiece.Attack();
  }
  else if(myPiece is MoveablePiece)
  {
    MoveablePiece moveablePiece = (MoveablePiece)myPiece;
    moveablePiece.Move();
    moveablePiece.Attack();
  }
  else if(myPiece is SceneryPiece)
  {
    //Scenery doesn't really do much...
  }
  else if(myPiece is GamePiece)
  {
    GamePiece gamePiece = (GamePiece)myPiece;
    System.Console.WriteLine(gamePiece.PieceDescription);
  }
  else
  {
    //myPiece is some other type
    throw new System.NotImplementedException();
  }


Anyway, don't know if this will assist with your exact circumstances, but hopefully sheds some light on the whole object-oriented thing!

- eternal
0
Never miss a deadline with monday.com

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

 
jatinderalaghCommented:
@ eternal_21 : That's nicely put up

@knowlton  : With this object oriented approach if you are intrested in using design pattern. I think you can use here Abstract Class Factory design pattern.
http://www.devhood.com/tutorials/tutorial_details.aspx?tutorial_id=654
http://www.codeguru.com/Csharp/.NET/net_general/patterns/article.php/c4673/

Cheers
jatinder
0
 
Tom KnowltonWeb developerAuthor Commented:
eternal:  WOW....very cool....I had no idea you could do it like that!  It will take me a little bit to completely understand it...but it seems very powerful and flexible.

jatinderalagh:  Thanks for the links to the patterns, I'll check them out.

dungla:  I moved the enum into the class proper, like you suggested...thanks.
0
 
Tom KnowltonWeb developerAuthor Commented:
//1 = MR
//2 = MED
//4 = ENG
//8 = DEST
//256 = WALL
//512 = EMPTY


Was my attempt to uniquely identify the game pieces so I could do things like:

MR * MED = 2
ENG * MED = 8
ENG * DEST = 32

So by looking at the product I could tell who was attacking who.  Come to think of it, there's no way to tell MR vs DEST    from   MED vs ENG.........hmmmm....
0
 
eternal_21Commented:
Instead of using the product, use the sum... I think you will have better results!

Another thing you could do is modify your Attack method as follows:

  public abstract void Attack(GamePiece gamePiece);

And then in your method implementations, you could look at the gamePiece object and determine who you were attacking, for example, if a MasterRobot attacked a Medic, use:

  MasterRobot masterRobot = new MasterRobot();
  Medic medic = new Medic();

  masterRobot.Attack(medic);

and then the masterRobot method could modify the medic's health, etc.  Of course, you would have to determine if gamePiece was an object that had health, so you could check if it was a MoveablePiece or a StationaryPiece...  if it was a SceneryPiece then throw an exception or something.
0
 
Tom KnowltonWeb developerAuthor Commented:
Good suggestions...yes....I want to setup the object so it responds appropriately to  an attack or advance from another game piece:

For example...if an Engineer were to "attack" a Wall....the Wall would not lose points but instead would be pushed in the direction the Engineer is moving.

If a Medic "attacks" one of its own pieces...the result would be "healing" that piece.

If a Destroyer or Engineer "attacks" an enemy piece....then that piece's health would be reduced by whatever damage the attacking piece can inflict.

I want to build this into each game piece so they just respond as they should without complicated logic.
0
 
eternal_21Commented:
Are these game peices on a grid like a chessboard?
0
 
Tom KnowltonWeb developerAuthor Commented:
Yes, a 10 x 10 board.

This may help you with understanding the game / rules.

http://www.robotzgame.com/default.asp?pagetoload=gamerules.asp
0
 
Tom KnowltonWeb developerAuthor Commented:
Can I have more than one Create method I override?

as in:

 public virtual void Create(string temp_owner,bool is_black)
          {

          }

 public virtual void Create(  )            //no params
           {

          }
0
 
eternal_21Commented:
Why are you using a "Create method"?  Why don't you just use the new keyword to initialize/create new game pieces?
0

Featured Post

Never miss a deadline with monday.com

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now