Archive for January, 2005

Strategy Pattern

Taking Alastair Patrick’s example of applying patterns to game making I present the strategy pattern. The strategy pattern encapsulates behavior allowing flexibility and object composition. This means a group of classes (as ships) can have some behavior in common, but also room for different flavors without having a lot of overrides and duplicate code. Here’s an example in code. Note, I’ve even written it so the behavior can be changed at runtime!

public interface AttackBehavior

{

void Attack();

}

public class AttackWithNoWeapon : AttackBehavior

{

public AttackWithNoWeapon()

{

//

// TODO: Add constructor logic here

//

}

public void Attack()

{

Console.Out.WriteLine( “No weapon to attack with.” );

}

}

public class AttackWithRedLaser : AttackBehavior

{

public AttackWithRedLaser()

{

//

// TODO: Add constructor logic here

//

}

public void Attack()

{

Console.Out.WriteLine( “Firing red lasers.” );

}

}

public class AttackWithReinforcedIceProjectile : AttackBehavior

{

public AttackWithReinforcedIceProjectile()

{

//

// TODO: Add constructor logic here

//

}

public void Attack()

{

Console.Out.WriteLine( “Firing Reinforced Ice Projectiles ” );

}

}

public interface FlyBehavior

{

void Fly();

}

public class FlyWithIonEngines : FlyBehavior

{

public FlyWithIonEngines()

{

//

// TODO: Add constructor logic here

//

}

public void Fly()

{

Console.Out.WriteLine( “Moved 4 parsecs.” );

}

}

public class FlyWithNoEngines : FlyBehavior

{

public FlyWithNoEngines()

{

//

// TODO: Add constructor logic here

//

}

public void Fly()

{

Console.Out.WriteLine( “Movement determined by gravity and inertia.” );

}

}

public class FlyWithSolarSails : FlyBehavior

{

public FlyWithSolarSails()

{

//

// TODO: Add constructor logic here

//

}

public void Fly()

{

Console.Out.WriteLine( “Low Solar Wind, move .5 parsecs.” );

}

}

public abstract class Ship

{

protected FlyBehavior flyBehavior = null;

protected AttackBehavior attackBehavior = null;

public Ship()

{

}

public abstract void Display();

public void PerformFly()

{

flyBehavior.Fly();

}

public void PerformAttack()

{

attackBehavior.Attack();

}

public void Repair()

{

Console.Out.WriteLine( “Repair teams dispatched, 10% improvement.” );

}

public void SetFlyBehavior( FlyBehavior fb )

{

flyBehavior = fb;

}

public void SetAttackBehavior( AttackBehavior ab )

{

attackBehavior = ab;

}

}

public class TerranMarkI : Ship

{

public TerranMarkI()

{

flyBehavior = new FlyWithIonEngines();

attackBehavior = new AttackWithRedLaser();

}

public override void Display()

{

Console.Out.WriteLine( “This is a Terran Mark I ship.” );

}

}

public class NeiwckronMkI : Ship

{

public NeiwckronMkI()

{

SetAttackBehavior( new AttackWithNoWeapon() );

SetFlyBehavior( new FlyWithIonEngines() );

}

public override void Display()

{

Console.Out.WriteLine( “A Neiwckron Mark I ship.” );

}

}

public class TheMain

{

///

/// The main entry point for the application.

///

[STAThread]

static void Main()

{

Ship s1 = new TerranMarkI();

s1.PerformAttack();

s1.PerformFly();

Ship s2 = new NeiwckronMkI();

s2.PerformFly();

s2.SetFlyBehavior( new FlyWithSolarSails() );

s2.PerformFly();

}

}

When I am not writing about making games, procrastinating or watching movies I’m working on a science fiction strategy game (sometimes called 4X or empire builder.) Email me questions, ideas and jokes at mrphil (at) mrphilgames . com

Read Full Post »

Object Cloning Using Reflection

Today on Alastair Patrick’s blog he shared a way to use reflection to clone objects which I found really interesting. I took the liberty of expanding the example a little so I could better understand the effects deep and shallow cloning had. I also replaced his main() with a NUnit test.

using System;

using System.Reflection;

using NUnit.Framework;

public class DeepCopyAttribute : Attribute

{

}

public class PrototypeCloner

{

public object Clone( object prototype )

{

object clone = CreateNewInstance( prototype.GetType() );

CopyPropertyValuesFromPrototype( clone, prototype );

return clone;

}

private object CreateNewInstance(Type type)

{

ConstructorInfo defaultConstructor = type.GetConstructor( new Type[0] );

return defaultConstructor.Invoke(new object[0]);

}

private bool IsPropertyDeepCopied( PropertyInfo property )

{

if( property.GetCustomAttributes( typeof(DeepCopyAttribute), true).Length

!= 0 )

{

return true;

}

else

{

return false;

}

}

private void CopyPropertyValuesFromPrototype(object clone, object prototype)

{

foreach (PropertyInfo property in prototype.GetType().GetProperties())

{

if (IsPropertyDeepCopied(property))

{

object prototypeProperty = property.GetValue(prototype, null);

object cloneProperty = Clone(prototypeProperty);

property.SetValue(clone, cloneProperty, null);

}

else

{

property.SetValue(clone, property.GetValue(prototype, null), null);

}

}

}

}

public abstract class Weapon

{

}

public class LaserDisruptor: Weapon

{

public float RateOfFire

{

get

{

return rateOfFire;

}

set

{

rateOfFire = value;

}

}

private float rateOfFire = 1000;

}

public abstract class Entity {}

public class SpaceMonkey: Entity

{

public float Speed

{

get

{

return speed;

}

set

{

speed = value;

}

}

private float speed = 5;

private Weapon deepCopyWeapon = new LaserDisruptor();

[DeepCopyAttribute()]

public Weapon DeepCopyWeapon

{

get

{

return deepCopyWeapon;

}

set

{

deepCopyWeapon = value;

}

}

private Weapon shallowCopyWeapon = new LaserDisruptor();

public Weapon ShallowCopyWeapon

{

get

{

return shallowCopyWeapon;

}

set

{

shallowCopyWeapon = value;

}

}

}

[TestFixture]

public class PrototypeClonerTests

{

[Test]

public void Clone()

{

SpaceMonkey prototypeObject = new SpaceMonkey();

PrototypeCloner cloner = new PrototypeCloner();

Entity clonedObject = null;

clonedObject = (Entity)cloner.Clone(prototypeObject);

SpaceMonkey clonedMonkey = (SpaceMonkey)clonedObject;

// Testing for Equality

Assert.AreEqual( prototypeObject.Speed, clonedMonkey.Speed, “Fail1” );

// — This is false because it is a Deep Copy property and this class

// doesn’t override the Equals or == operator so the Assert’s

// AreSame and AreEqual tests have the identical result

Assert.IsFalse(

prototypeObject.DeepCopyWeapon == clonedMonkey.DeepCopyWeapon,

“Fail2” );

Assert.AreEqual(

((LaserDisruptor)prototypeObject.DeepCopyWeapon).RateOfFire,

((LaserDisruptor)clonedMonkey.DeepCopyWeapon).RateOfFire, “Fail3” );

// — Are equal, as in the same object because it is the Shallow Copy

Assert.AreEqual( prototypeObject.ShallowCopyWeapon,

clonedMonkey.ShallowCopyWeapon,

“Fail4” );

Assert.AreEqual(

((LaserDisruptor)prototypeObject.ShallowCopyWeapon).RateOfFire,

((LaserDisruptor)clonedMonkey.ShallowCopyWeapon).RateOfFire, “Fail5” );

// Testing for Same

// — Only the DeepCopyAttribute properties should be the same

// — Not same because it is not a referance object (float)

Assert.IsFalse(

object.ReferenceEquals( prototypeObject.Speed, clonedMonkey.Speed ),

“Fail6” );

// — Not same because it is a DeepCopyAttribute

Assert.IsFalse(

object.ReferenceEquals( prototypeObject.DeepCopyWeapon,

clonedMonkey.DeepCopyWeapon),

“Fail7” );

// — Not same because it is not a referance object (float)

Assert.IsFalse( object.ReferenceEquals(

((LaserDisruptor)prototypeObject.DeepCopyWeapon).RateOfFire,

((LaserDisruptor)clonedMonkey.DeepCopyWeapon).RateOfFire), “Fail8” );

// — The only referance object that isn’t a deep copy Are Same

Assert.AreSame( prototypeObject.ShallowCopyWeapon,

clonedMonkey.ShallowCopyWeapon,

“Fail9” );

// — Not same because it is not a referance object (float)

Assert.IsFalse( object.ReferenceEquals(

((LaserDisruptor)prototypeObject.ShallowCopyWeapon).RateOfFire,

((LaserDisruptor)clonedMonkey.ShallowCopyWeapon).RateOfFire),

“Fail10” );

}

}

Read Full Post »