DateTimeAssert

June 2nd, 2015

Comparing date times in a unit test can be a challenging thing. Because the date time is most of the time an external dependency you don’t fully control, it can be desirable to allow some tolerance. Unfortunately, both the Microsoft Visual Studio Tools and the NUnit Framwork don’t support such a thing out of the box. Another issue can be that the fail messages use the ToString method of objects, and the default implementation of a date time does not reveal the milliseconds, which can be quiet annoying.

This implementation gives you both the option to specify a tolerance, as it shows the milliseconds of the expected and actual date time. It even shows the difference between the two if the tolerance is bigger than zero.

using NUnit.Framework;
using System;
using System.Diagnostics;
using System.Text;

namespace Qowaiv.UnitTests.TestTools
{
	public static class DateTimeAssert
	{
		/// <summary>Verifies that two date times are equal. Two date times are
		/// considered equal if both are null, or have the same value.
		/// If they are not equal an NUnit.Framework.AssertionException is
		/// thrown.
		/// </summary>
		/// <param name="expected">
		/// The date time that is expected
		/// </param>
		/// <param name="actual">
		/// The actual date time
		/// </param>
		/// <param name="tolerance">
		/// The accepted tolerance between the actual and expected date time.
		/// </param>
		/// <param name="message">
		/// The message to display in case of failure.
		/// </param>
		/// <param name="args">
		/// Array of objects to be used in formatting the message
		/// </param>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// If the specified tolerance is negative.
		/// </exception>
		/// <exception cref="NUnit.Framework.AssertionException">
		/// If the assertion fails.
		/// </exception>
		[DebuggerStepThrough]
		public static void AreEqual(DateTime? expected, DateTime? actual, string message, params object[] args)
		{
			AreEqual(expected, actual, TimeSpan.Zero, message, args);
		}

		/// <summary>Verifies that two date times are equal. Two date times are
		/// considered equal if both are null, or the difference between the
		/// date times is smaller then the specified tolerance.
		/// If they are not equal an NUnit.Framework.AssertionException is
		/// thrown.
		/// </summary>
		/// <param name="expected">
		/// The date time that is expected
		/// </param>
		/// <param name="actual">
		/// The actual date time
		/// </param>
		/// <param name="tolerance">
		/// The accepted tolerance between the actual and expected date time.
		/// </param>
		/// <param name="message">
		/// The message to display in case of failure.
		/// </param>
		/// <param name="args">
		/// Array of objects to be used in formatting the message
		/// </param>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// If the specified tolerance is negative.
		/// </exception>
		/// <exception cref="NUnit.Framework.AssertionException">
		/// If the assertion fails.
		/// </exception>
		[DebuggerStepThrough]
		public static void AreEqual(DateTime? expected, DateTime? actual, TimeSpan tolerance, string message, params object[] args)
		{
			Guard.NotNegative(tolerance, "tolerance");

			if (actual.HasValue && expected.HasValue)
			{
				var difference = (actual.Value - expected.Value).Duration();

				if (difference > tolerance)
				{
					var sb = new StringBuilder();
					sb.AppendFormat("Expected:<{0:yyyy-MM-dd HH:mm:ss.FFFFFFF}>. ", actual);
					sb.AppendFormat("Actual:<{0:yyyy-MM-dd HH:mm:ss.FFFFFFF}>.", expected);
					if (tolerance > TimeSpan.Zero)
					{
						sb.AppendFormat(" Difference:<{0:#,##0.0######} seconds>.", difference.TotalSeconds);
					}
					if (!String.IsNullOrEmpty(message))
					{
						sb.Append(' ').AppendFormat(message, args);
					}
					Assert.Fail(sb.ToString());
				}
			}
			else
			{
				Assert.AreEqual(expected, actual, message, args);
			}
		}
	}
}

If programming languages were weapons

December 4th, 2014

Debugger display attribute

November 19th, 2014

Every developer has to debug the code he is working on. You IDE helps you by showing your local variable watches and, allowing you to define watches. Those watches are the ToString() representation of the objects/expressions you watch.

That’s nice, but you can improve this experience a lot by defining your own debugger display! Of course, there some types in .NET that show you more than the default implementation of object (ToString(){ return GetType().FullName; }), but for you own classes, this is the behavior, by default.

So, what to do? Well, you can implement ToString() for your class, but there are a lot of scenario’s (take ToString() for XDocument) where the result of ToString() is not what you want for your debugger display. Fortunately, .NET gives you the opportunity to add the DebuggerDisplayAttribute. This attrubute decorates your class with an instruction so that your debugger knows what to do, when showing a watch on it.

Basically, you can write a full expression in the attribute constructor argument, but referring to a (non public) property is more convenient. You can refer to a method too, but this can lead to unintended behavior. The expression is evaluated by the runtime and debugging a VB.NET project will give you a warning “Because the evaluation could cause side effects, it will not be executed, until enabled by the user”, even if the debugger display is implemented in C#. Using properties will not lead to this kind of situations.

For most situations you want to return a string, but you can return other objects as well. The IDE will call the DebuggerDisplay of that object. The cases where this can be of value, is where you want to show 17 instead of “17”. Most of the time returning a string is what you want.

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;

namespace HelloWorld
{
	[DebuggerDisplay("{DebuggerDisplay}")]
	public class DebuggerDisplayClassStringProperty
	{
		public int Number { get; set; }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private string DebuggerDisplay { get { return this.Number.ToString(CultureInfo.InvariantCulture); } }
	}

	[DebuggerDisplay("{DebuggerDisplay()}")]
	public class DebuggerDisplayClassStringMethod
	{
		public int Number { get; set; }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private string DebuggerDisplay() { return this.Number.ToString(CultureInfo.InvariantCulture); }
	}

	[DebuggerDisplay("{DebuggerDisplay}")]
	public class DebuggerDisplayClassObjectProperty
	{
		public int Number { get; set; }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private int DebuggerDisplay { get { return this.Number; } }
	}

	[DebuggerDisplay("{DebuggerDisplay()}")]
	public class DebuggerDisplayClassObjectMethod
	{
		public int Number { get; set; }

		[ExcludeFromCodeCoverage]
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private int DebuggerDisplay() { return this.Number; }
	}
}

If you want to create unit tests for it, is up to you, and if you expose the property publicly. If you don’t want to unit test the functionality, I recommend the use of the ExcludeFromCodeCoverage attribute. If you want to test it, you can use reflection.

using NUnit.Framework;
using System.Reflection;

namespace HelloWorld.UnitTests
{
	[TestFixture]
	public class DebuggerDisplayClassTest
	{
		[Test]
		public void DebuggerDisplay_StringMethod_String17()
		{
			var cls = new DebuggerDisplayClassStringMethod() { Number = 17 };
			var dd = cls.GetType().GetMethod("DebuggerDisplay", BindingFlags.Instance | BindingFlags.NonPublic);

			var act = dd.Invoke(cls, new object[0]);
			var exp = "17";

			Assert.AreEqual(exp, act);
		}

		[Test]
		public void DebuggerDisplay_ObjectMethod_Int17()
		{
			var cls = new DebuggerDisplayClassObjectMethod() { Number = 17 };
			var dd = cls.GetType().GetMethod("DebuggerDisplay", BindingFlags.Instance | BindingFlags.NonPublic);

			var act = dd.Invoke(cls, new object[0]);
			var exp = 17;

			Assert.AreEqual(exp, act);
		}

		[Test]
		public void DebuggerDisplay_StringProperty_String17()
		{
			var cls = new DebuggerDisplayClassStringProperty() { Number = 17 };
			var dd = cls.GetType().GetProperty("DebuggerDisplay", BindingFlags.Instance | BindingFlags.NonPublic);

			var act = dd.GetValue(cls);
			var exp = "17";

			Assert.AreEqual(exp, act);
		}

		[Test]
		public void DebuggerDisplay_ObjectProperty_Int17()
		{
			var cls = new DebuggerDisplayClassObjectProperty() { Number = 17 };
			var dd = cls.GetType().GetProperty("DebuggerDisplay", BindingFlags.Instance | BindingFlags.NonPublic);

			var act = dd.GetValue(cls);
			var exp = 17;

			Assert.AreEqual(exp, act);
		}
	}
}

Example of handling cookies Domain-driven

November 5th, 2014

A lot of web applications use cookies for enabling special features. For my company (Exact) for example, we use a cookie to store the latest division (code) that was selected by a user. This is not modelled DDD right now. How should it look like if it did? How to tackle the fact that an HTTP cookie is sealed, as we don’t want to introduce some (static) tooling or helper class?

First of all, I created an abstract wrapped cookie class. This allows developers to add extra (factory) methods, constructors, and properties based on the domain where the specific cookie has to be used. A cookie is a quiet generic thing, and we want to model domain specific implementations. Note that, as a result of that, the Value and Values property of the HTTP cookie are not publicly exposed.

In this case, the base class has besides the required wrapping a constructor with a user ID (GUID) and a static method to create a cookie name, as that is the default for our company. That is not necessarily something everybody should need.

For the specific cookie we have one public constructor, that creates an instance based on the user ID and its division code (a custom value object, containing that logic). Because, in the end, that is what this cookie is all about in our domain.

In this case it is extremely important that is always possible to (implicitly) cast between the wrapped and the original cookie. That makes the usage if the class way more easy.

using System;
using System.Web;

namespace HelloWorld.Web
{
	/// <summary>Represents a cookie that stores the last division visited by an user (ID).</summary>
	public class DivisionCodeCookie : WrappedCookie
	{
		/// <summary>The key for the division code.</summary>
		private const string DivisionCodeKey = "Division";

		/// <summary>Initializes a new division code cookie.</summary>
		/// <param name="userId">The user ID.</param>
		/// <param name="code">The division code.</param>
		public DivisionCodeCookie(Guid userId, DivisionCode code)
			: base(userId)
		{
			this.Code = code;
		}

		/// <summary>Initializes a new division code cookie.</summary>
		private DivisionCodeCookie(HttpCookie cookie) : base(cookie) { }

		/// <summary>Gets and set the division code of the cookie.</summary>
		public DivisionCode Code
		{
			get { return DivisionCode.TryParse(UnderlingCookie.Values[DivisionCodeKey]); }
			set { UnderlingCookie.Values[DivisionCodeKey] = value.ToString(); }
		}

		/// <summary>Creates a copy of the division code cookie.</summary>
		public DivisionCodeCookie Copy() { return new DivisionCodeCookie(this.UserId, this.Code); }

		/// <summary>Casts an HTTP Cookie to a Division code cookie.</summary>
		/// <remarks>
		/// Making the cast implicit allows the use of wrapped cookie when a HTTP cookie is asked.
		/// </remarks>
		public static implicit operator DivisionCodeCookie(HttpCookie http) { return new DivisionCodeCookie(http); }
	}
}

The base class.

using System;
using System.Diagnostics;
using System.Web;

namespace HelloWorld.Web
{
	/// <summary>Represents a cookie.</summary>
	/// <remarks>
	/// It is a wrapper that allows to add custom logic to the cookie.
	/// </remarks>
	[DebuggerDisplay("{DebuggerDisplay}")]
	public abstract class WrappedCookie
	{
		/// <summary>Initials a new wrapped cookie based on an HTTP cookie.</summary>
		protected WrappedCookie(HttpCookie httpCookie)
		{
			if (httpCookie == null) { throw new ArgumentNullException("httpCookie"); }
			this.UnderlingCookie = httpCookie;
		}

		/// <summary>Initials a new wrapped cookie based on an user ID.</summary>
		protected WrappedCookie(Guid userId) : this(GetCookieName(userId)) { }

		/// <summary>Initials a new wrapped cookie based on cookie name.</summary>
		protected WrappedCookie(string name): this(new HttpCookie(name)){}

		/// <summary>Gets or set the underlying HTTP cookie.</summary>
		protected HttpCookie UnderlingCookie { get; set; }

		/// <summary>Gets or set the user ID of the cookie.</summary>
		public Guid UserId
		{
			get
			{
				Guid userid;

				if (this.Name.StartsWith("ExactServer{")&& Guid.TryParseExact(this.Name.Substring(11), "B", out userid))
				{
					return userid;
				}
				return Guid.Empty;

			}
			set { this.Name = GetCookieName(value); }
		}

		/// <summary>Gets or set the name of the cookie.</summary>
		public string Name 
		{ 
			get { return UnderlingCookie.Name;  }
			set{ UnderlingCookie.Name = value;}
		}
		/// <summary>Gets or set the domain of the cookie.</summary>
		public string Domain
		{
			get { return UnderlingCookie.Domain; }
			set { UnderlingCookie.Domain = value; }
		}
		/// <summary>Gets or set the path of the cookie.</summary>
		public string Path
		{
			get { return UnderlingCookie.Path; }
			set { UnderlingCookie.Path = value; }
		}
		/// <summary>Gets or set the expiration date of the cookie.</summary>
		public DateTime Expires
		{
			get { return UnderlingCookie.Expires; }
			set { UnderlingCookie.Expires = value; }
		}
		
		/// <summary>Gets or set a value that specifies whatever a cookie is accessible by client-side script.</summary>
		public bool HttpOnly
		{
			get { return UnderlingCookie.HttpOnly; }
			set { UnderlingCookie.HttpOnly = value; }
		}
		/// <summary>Gets or set a value indicating specifies whatever to transmit the cookie Secure Sockets Layers (SSL)--that is, over HTTPS only.</summary>
		public bool Secure
		{
			get { return UnderlingCookie.Secure; }
			set { UnderlingCookie.Secure = value; }
		}
		/// <summary>Determines whatever the cookie is allowed to participate in output caching.</summary>
		public bool Shareable
		{
			get { return UnderlingCookie.Shareable; }
			set { UnderlingCookie.Shareable = value; }
		}

		/// <summary>Casts a wrapped cookie (back) to an HTTP cookie.</summary>
		/// <remarks>
		/// Making the cast implicit allows the use of wrapped cookie when an HTTP cookie is asked.
		/// </remarks>
		public static implicit operator HttpCookie(WrappedCookie wrapped) { return wrapped.UnderlingCookie; }

		/// <summary>Cleans the cookie up by clearing the value and set the expire date in the past.</summary>
		public void Cleanup()
		{
			UnderlingCookie.Expires = DateTime.Now.AddMinutes(-1);
			UnderlingCookie.Values.Clear();
		}

		/// <summary>Gets the name for the cookie based on the user ID.</summary>
		public static string GetCookieName(Guid userId)
		{
			return string.Format("ExactServer{0:B}", userId);
		}

		/// <summary>Gets a debugger display for the wrapped cookie.</summary>
		protected virtual string DebuggerDisplay { get { return string.Format("Cookie[{0}], Value: {1}, Expires: {2:yyyy-MM-dd HH:mm}", this.Name, this.UnderlingCookie.Value, this.Expires); } }
	}
}

Tech Days 2014 – Shared thoughts

April 24th, 2014

14 tech·days

Two days of new technology

Last week, I went to the tech days. I would like to share some thoughts and impressions with you. Feel free to comment, share your own thoughts.

wo 16 april 2014

Massive.js [key note]

Erich Gamma showed Monaco, the online development platform of Microsoft. It was quite impressive to see such a huge application written in JavaScript. The focus of the talk was in how to manage this code. The Monaco team faced big issues passing the 100kloc. JavaScript is really (too) forgiving. That, combined with the lack of interfacing and strong typing, the introduced TypeScript. The concluded that TypeScript saved there day, and should always be considered when you’re adding more than just some lines of JavaScript code.

Some TV crew took my by surprise after the session:

New and improved ASP.NET MVC 5/Web API2

This was a nice – and nothing more – talk about the new MVC(5) stuff. MS added a nice feature to MVC5/API2: The routing attribute, a real improvement imho. The focus was more on Web API, than on MVC. I think this is a good thing. Personally I would not recommend the use of MVC in the traditional serverside approach at all. Furthermore, I would not be surprised if Web API 3 (or a later version) will be released independent from MVC (or without).

Fundamentals guide to HDP & HDInsight

This talk was about HaDoop, an open source framework developed by Yahoo. It allows you to analyze Big Data using map/reduce. For .NET the allow querying using Linq. Really interesting stuff!

See: hadoopsdk.codeplex.com

Using NuGet the way you should

I was really looking forward to this this session, and it was a nice talk. Unfortunately (due to continuous crashes of Visual Studio) there was not enough time for questions (I had some). In general: if you distribute code and/or libraries use NuGet. Not only because it can speed up development (by reducing the compilation time), it also forces you to think about versioning and separating concerns.

Using Orleans for building scalable cloud applications

Orleans is a MS platform that is developed to make (the game) Helo more scalable. The way it works by creating factories (Silo) that supply proxy references (grains) to the actual instances. Orleans decides where the real instances live, and can handle the up and down scaling. It is a powerful framework, but more suited for applications like Helo, than for applications like Exact Online. But maybe at some point It can become interesting for us too.

See: research.microsoft.com/en-us/projects/orleans

do 17 april 2014

Xamarin: native apps for iOS/Android in C#

Two years ago, I went to a Xamarin session, and I was quite impressed. I downloaded it and tried to get started. Unfortunately, the evaluation version was so limited that you hardly could do anything with it. What a deception that this is still the case. Furthermore, the development experience for iOS is still (a little) hell. But, and that is the good thing, what can be accomplished with Xamarin is great. If you have to develop native apps on commercial base, Xamarin should be your choice if you have a C# background.

Using MVVM so you can write better code and less code

MVVM (Model-View-View model) is approach that has a lot of strong advocates. In fact, a lot of client side frameworks (like Knockout and AngularJS) are MVVM. This session used (server side) MVC.NET, but it was not about the framework, it was about the methodology (and that was good!). The view model is the key in this approach. It fills the gap between the model (and data) and the view. It handles changes of the model (and therefor the view). When the view model changes, it notifies the view, so that it can update, if the view needed new data, the view model delivers. Calculations needed for the view are done by the view model too. The claim of the speaker that this results in less code is discussable, the first one is not. This will definably improve your code quality! If you did not already use this pattern, you start using it right now.

Event processing at all scales with RX

Bart De Smet is a phenomenon. If he speaks somewhere, I always want to be at least at one of his talks. Therefor I ended up at an RX (Reactive Extensions). De Smets is working on Cortana, an digital assistant or Windows Phone. It serializes expressions and enables the handling of the RX events in the cloud (Azure). It was a nice talk, but not as good (imho) as earlier talks I went to. Still, if you missed it, you should watch the video.

Games Services and Telemetry Processing in MS Azure

I have to admit that I was triggered by “Games”, but in fact this talk was on telemetry processing, and not on gaming. It was really good talk, and showed some nice ways of processing Big Data using Azure.

Conclusions

By going to the cloud (whatever cloud that is) means that code becomes async. Depending on you requirements, data, events, and/or computing can be moved to the cloud. There are no free lunches there, so you always have to do the math first.

JavaScript is only becoming more important, so get your hands dirty and do a lot of JS coding. Try AngularJS, Knockout (also for hands on experience on MVVM), and of course TypeScript.

Truusje my lovely ant(s)

December 24th, 2011

Who would have told me two month ago that I would be number one of the Netherlands at a massive AI programming Challenge, and number 72 of 7897, would not have been taken that seriously. But he (or she) would have been right. Although the number of 7897 is one of debate: It includes a lot of starter bots (bots with hardly or no participant effort).

Loading..

NB: I used my own firstname Corniel during the contest

It was my good friend (and colleague) JCK who found out about the contest, at a Friday afternoon. With not that much work left we both gave it a try immediately. JCK started with an simple but good working implementation of diffusion. It worked quite good and his first version peaked at 145 and outplayed me completely.

I did things different. At second I decided to rewrite the starter kit. I just wanted to know what was going on. But off course I started with a name: Truusje!

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

namespace HelloWorld.Ants
{
public class Instruction : IEquatable&amp;amp;lt;Instruction&amp;amp;gt;
{
/// &lt;summary&gt;Represents the GO instruction.&lt;/summary&gt;
public static readonly Instruction Go = new Instruction() { Type = InstructionType.go };
/// &lt;summary&gt;Represents the READY instruction.&lt;/summary&gt;
public static readonly Instruction Ready = new Instruction() { Type = InstructionType.ready };
/// &lt;summary&gt;Represents the END instruction.&lt;/summary&gt;
public static readonly Instruction End = new Instruction() { Type = InstructionType.end };

/// &lt;summary&gt;Constructor.&lt;/summary&gt;
/// &lt;remarks&gt;Sets some defaults.&lt;/remarks&gt;
private Instruction()
{
this.Value = -1;
this.Row = -1;
this.Col = -1;
this.Color = AntsColor.None;
}

/// &lt;summary&gt;Gets and set the type.&lt;/summary&gt;
public InstructionType Type { get; set; }

/// &lt;summary&gt;Gets and set the value.&lt;/summary&gt;
public long Value { get; set; }

/// &lt;summary&gt;Gets and set the row.&lt;/summary&gt;
public int Row { get; set; }

/// &lt;summary&gt;Gets and set the column.&lt;/summary&gt;
public int Col { get; set; }

/// &lt;summary&gt;Gets and set the color.&lt;/summary&gt;
public int Color { get; set; }

/// &lt;summary&gt;Gets and set the dirction.&lt;/summary&gt;
public DirectionType Direction { get; set; }

/// &lt;summary&gt;Represents the instruction as System.String.&lt;/summary&gt;
/// &lt;remarks&gt;
/// The ToString is equal to the parsed input or required output.
/// &lt;/remarks&gt;
public override string ToString()
{
var sb = new StringBuilder();
sb.Append(this.Type);
if (this.Value &amp;amp;lt;= 0)
{
sb.Append(' ').Append(this.Value);
}
else if (this.Row &amp;amp;lt;= 0 &amp;amp;amp;amp;&amp;amp;amp;amp; this.Col &amp;amp;lt;= 0)
{
sb.Append(' ').Append(this.Row).Append(' ').Append(this.Col);
if (this.Color &amp;amp;lt;= AntsColor.Own)
{
sb.Append(' ').Append(this.Color);
}
else if (this.Direction != DirectionType.X)
{
sb.Append(' ').Append(this.Direction);
}
}
return sb.ToString();
}

/// &lt;summary&gt;Gets a hash code.&lt;/summary&gt;
public override int GetHashCode()
{
return ToString().GetHashCode();
}

/// &lt;summary&gt;Implements equals.&lt;/summary&gt;
public override bool Equals(object obj)
{
if (obj is Instruction)
{
return Equals((Instruction)obj);
}
return false;
}

/// &lt;summary&gt;Implements equals.&lt;/summary&gt;
public bool Equals(Instruction other)
{
if (object.Equals(other, null)) { return false; }
return
this.Type == other.Type &amp;amp;amp;amp;&amp;amp;amp;amp;
this.Value == other.Value &amp;amp;amp;amp;&amp;amp;amp;amp;
this.Row == other.Row &amp;amp;amp;amp;&amp;amp;amp;amp;
this.Col == other.Col &amp;amp;amp;amp;&amp;amp;amp;amp;
this.Color == other.Color;
}

/// &lt;summary&gt;Equals operator.&lt;/summary&gt;
public static bool operator ==(Instruction inst0, Instruction inst1)
{
if (!object.Equals(inst0, null))
{
return inst0.Equals(inst1);
}
return object.Equals(inst1, null);
}
/// &lt;summary&gt;Don't equals operator.&lt;/summary&gt;
public static bool operator !=(Instruction inst0, Instruction inst1)
{
return !(inst0 == inst1);
}

/// &lt;summary&gt;Parses an instruction.&lt;/summary&gt;
public static Instruction Parse(string line)
{
var instr = new Instruction();
var tp = InstructionType.None;

string[] tokens = line.Split();

if (tokens.Length &amp;amp;lt; 0)
{
tp = (InstructionType)Enum.Parse(typeof(InstructionType), tokens[0]);

if (TokenLength[tp] == tokens.Length)
{
if (tokens.Length == 2)
{
if (tp == InstructionType.player_seed)
{
instr.Value = long.Parse(tokens[1]);
}
else
{
instr.Value = (int)uint.Parse(tokens[1]);
}
}
if (tokens.Length == 4)
{
if (tp == InstructionType.o)
{
instr.Direction = (DirectionType)Enum.Parse(typeof(DirectionType), tokens[3]);
}
else
{
instr.Color = (int)uint.Parse(tokens[3]);
}
}
if (tokens.Length == 3 || tokens.Length == 4)
{
instr.Row = (int)uint.Parse(tokens[1]);
instr.Col = (int)uint.Parse(tokens[2]);
}

instr.Type = tp;
return instr;
}
}
throw new ArgumentException(string.Format("The line '{0}' is not a valid instruction.", line));
}

/// &lt;summary&gt;Parses a multi line input.&lt;/summary&gt;
public static List&amp;amp;lt;Instruction&amp;amp;gt; ParseMultiLine(string text)
{
var list = new List&amp;amp;lt;Instruction&amp;amp;gt;();

var lines = text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);

foreach(var line in lines)
{
list.Add(Instruction.Parse(line));
}
return list;
}

/// &lt;summary&gt;Creates a move based on a row, a column and a direction.&lt;/summary&gt;
public static Instruction CreateMove(int row, int col, DirectionType dir)
{
return new Instruction()
{
Type = InstructionType.o,
Row = row,
Col = col,
Direction = dir,
};
}

/// &lt;summary&gt;Helper for parsing instructions.&lt;/summary&gt;
private static Dictionary&amp;amp;lt;InstructionType, int&amp;amp;gt; TokenLength = new Dictionary&amp;amp;lt;InstructionType, int&amp;amp;gt;()
{
{ InstructionType.None, 0 },

{ InstructionType.ready, 1 },
{ InstructionType.go, 1 },
{ InstructionType.end, 1 },

{ InstructionType.player_seed, 2 },
{ InstructionType.players, 2 },
{ InstructionType.cols, 2 },
{ InstructionType.rows, 2 },
{ InstructionType.turntime, 2 },
{ InstructionType.loadtime, 2 },
{ InstructionType.viewradius2, 2 },
{ InstructionType.attackradius2, 2 },
{ InstructionType.spawnradius2, 2 },

{ InstructionType.turn, 2 },
{ InstructionType.turns, 2 },

{ InstructionType.f, 3 },
{ InstructionType.r, 3 },
{ InstructionType.w, 3 },
{ InstructionType.d, 4 },
{ InstructionType.a, 4 },
{ InstructionType.h, 4 },

{ InstructionType.o, 4 },
};
}
}

I guess that a lot of developers would argue that this is gold plating to the limit, it worked for me. The other changes were not half as big as this one.

Then the real coding could start. So lets get dirty. I went for a multiple strategy pattern. A lot of strategies that give there advises, and it was up to a picking mechanism to pick the best and apply them. On the way I noticed that I had a strict order in which I’d like to do my moves. So I tweaked the pattern. From then on, My strategies had a hierarchy. Only if higher at the hierarchy no move came trough a strategy could give its advise.

A second big change came when I moved the state to a strategy. I introduced some extra events (triggered at the old fashioned way, just by calling it directly) and gave direct access to this strategy (and the combat and queue strategy). The abstract base Strategy ended op this way:

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

namespace HelloWorld.Ants
{
public abstract class Strategy
{
/// &lt;summary&gt;Constructor.&amp;amp;lt;/summary&amp;amp;lt;
/// &amp;amp;lt;param name=&amp;amp;amp;amp;amp;amp;quot;bot&amp;amp;amp;amp;amp;amp;quot;&amp;amp;lt;The underlying bot.&amp;amp;lt;/param&amp;amp;lt;
protected Strategy(Truusje bot)
{
this.Bot = bot;
}

/// &lt;summary&gt;Gets the underlying bot.&amp;amp;lt;/summary&amp;amp;lt;
public Truusje Bot { get; protected set; }

/// &lt;summary&gt;Gets the (main) score table.&amp;amp;lt;/summary&amp;amp;lt;
public int[,] Scores { get; protected set; }

/// &lt;summary&gt;Gives true if the score table represents distances, otherwise false.&amp;amp;lt;/summary&amp;amp;lt;
protected abstract bool ScoresAreDistances { get; }

/// &lt;summary&gt;Initializes the strategy.&amp;amp;lt;/summary&amp;amp;lt;
public virtual void Initialize()
{
this.Scores = Map.New&amp;amp;lt;int&amp;amp;gt;(Bot.Settings);
}

/// &lt;summary&gt;Handles the UpdateInit.&amp;amp;lt;/summary&amp;amp;lt;
public virtual void OnUpdateInit() { }

/// &lt;summary&gt;Handles the UpdateFood.&amp;amp;lt;/summary&amp;amp;lt;
public virtual AntsFood OnUpdateFood(AntsFood food) { return food; }
/// &lt;summary&gt;Handles the UpdateWater.&amp;amp;lt;/summary&amp;amp;lt;
public virtual AntsWater OnUpdateWater(AntsWater water) { return water; }

/// &lt;summary&gt;Handles the UpdateOwnHill.&amp;amp;lt;/summary&amp;amp;lt;
public virtual AntsHill OnUpdateOwnHill(AntsHill hill) { return hill; }
/// &lt;summary&gt;Handles the UpdateEnemyHill.&amp;amp;lt;/summary&amp;amp;lt;
public virtual AntsHill OnUpdateEnemyHill(AntsHill hill) { return hill; }

/// &lt;summary&gt;Handles the UpdateOwnAnt.&amp;amp;lt;/summary&amp;amp;lt;
public virtual AntsAnt OnUpdateOwnAnt(AntsAnt ant) { return ant; }
/// &lt;summary&gt;Handles the UpdateEnemyAnt.&amp;amp;lt;/summary&amp;amp;lt;
public virtual AntsAnt OnUpdateEnemyAnt(AntsAnt ant) { return ant; }

/// &lt;summary&gt;Handles the UpdateAfter.&amp;amp;lt;/summary&amp;amp;lt;
public virtual void OnUpdateAfter() { }

/// &lt;summary&gt;Handles the TurnInit.&amp;amp;lt;/summary&amp;amp;lt;
public virtual void OnTurnInit() { }

/// &lt;summary&gt;Handles the TurnAfterStrategy.&amp;amp;lt;/summary&amp;amp;lt;
/// &amp;amp;lt;remarks&amp;amp;lt;
/// This one is called for an ant that uses this strategy.
/// &amp;amp;lt;/remarks&amp;amp;lt;
public virtual void OnTurnAfterStrategy(AntsLoc oldLoc, AntsLoc newLoc, DirectionType dir, TruusjeCandidateMove move) { }
/// &lt;summary&gt;Handles the TurnAfterStrategy.&amp;amp;lt;/summary&amp;amp;lt;
/// &amp;amp;lt;remarks&amp;amp;lt;
/// This one is called for every ant that moved.
/// &amp;amp;lt;/remarks&amp;amp;lt;
public virtual void OnTurnAfter(AntsLoc oldLoc, AntsLoc newLoc, DirectionType dir, TruusjeCandidateMove move) { }
/// &lt;summary&gt;Handles the TurnFinish.&amp;amp;lt;/summary&amp;amp;lt;
/// &amp;amp;lt;remarks&amp;amp;lt;
/// At on turn finish extra work can be done that is not required but
/// useful. It should handle the time management as strict and safe
/// as possible.
/// &amp;amp;lt;/remarks&amp;amp;lt;
public virtual void OnTurnFinish() { }

/// &lt;summary&gt;Returns true if the strategy can give a move, otherwise false.&amp;amp;lt;/summary&amp;amp;lt;
public abstract bool CanMove(AntsAnt ant, AntsLoc loc, DirectionType dir);
/// &lt;summary&gt;Gets a move.&amp;amp;lt;/summary&amp;amp;lt;
public abstract TruusjeCandidateMove GetMove(AntsAnt ant, AntsLoc loc, DirectionType dir);

/// &lt;summary&gt;Creates a candidate move.&amp;amp;lt;/summary&amp;amp;lt;
/// &amp;amp;lt;remarks&amp;amp;lt;
/// Binds to the strategy.
/// &amp;amp;lt;/remarks&amp;amp;lt;
public virtual TruusjeCandidateMove CreateMove(AntsAnt ant, AntsLoc loc, DirectionType dir, int score, AntsAntType type)
{
return new TruusjeCandidateMove(ant, loc, dir, score, type, this);
}

/// &lt;summary&gt;Breaks on a condition.&amp;amp;lt;/summary&amp;amp;lt;
public void BreakWhen(AntsLoc loc, int r, int c, bool condition)
{
BreakWhen(loc.Row == r &amp;amp;amp;amp;&amp;amp;amp;amp; loc.Col == c &amp;amp;amp;amp;&amp;amp;amp;amp; condition);
}
/// &lt;summary&gt;Breaks on a condition.&amp;amp;lt;/summary&amp;amp;lt;
public void BreakWhen(int turn, AntsLoc loc, int r, int c)
{
BreakWhen(turn, loc.Row == r &amp;amp;amp;amp;&amp;amp;amp;amp; loc.Col == c);
}
/// &lt;summary&gt;Breaks on a condition.&amp;amp;lt;/summary&amp;amp;lt;
public void BreakWhen(int turn, bool condition)
{
BreakWhen(Bot.Turn == turn &amp;amp;amp;amp;&amp;amp;amp;amp; condition);
}
/// &lt;summary&gt;Breaks on a condition.&amp;amp;lt;/summary&amp;amp;lt;
/// &amp;amp;lt;remarks&amp;amp;lt;
/// Work around as conditional breakpoints are just way to slow, with thanks to JCK.
/// &amp;amp;lt;/remarks&amp;amp;lt;
public void BreakWhen(bool condition)
{
#if DEBUG
if (condition)
{
if (System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Break();
}
}
#endif
}
}

/// &lt;summary&gt;Extenions.&amp;amp;lt;/summary&amp;amp;lt;
public static class StrategyExtensions
{
/// &lt;summary&gt;Gets a specific strategy from the list.&amp;amp;lt;/summary&amp;amp;lt;
public static T Get&amp;amp;lt;T&amp;amp;gt;(this IEnumerable&amp;amp;lt;Strategy&amp;amp;gt; strategies) where T : Strategy
{
return (T)strategies.First(str =&amp;amp;gt; str.GetType() == typeof(T));
}
}
}

Okay, I had my own framework, and my giant strategy pattern, but dude, Truusje needs things to DO. Walk to food and so on. So I needed directions. A lot of people talked about A*, and that they had implemented that. Some thought of themselves as extremely smart, because they did. I did’t. Because I’m not that smart, and more importent: I saw no need for it (yet). So what did I do to determine were to go?

First of all I came to the conclusion that long distance planning (in two ways) was not useful. You just can’t look that far into the future. Secondly I noticed that A* was designed to find the smallest distance from one (or a small set of) point(s), to another. That’s not where I was searching for. At forehand, I had no clue where to go.

For my food strategy therefore I just kept stepping away in all directions from all food I knew, updating the score map, in this case actually representing the distance to a food, and I stopped searching when I found a ant. I had wild plans of fine tuning this, with complex assignment strategies. Even starting complex and switching to more simple during the game (as I needed my time to calculate other things and with optimization was not that much to win anymore)

For hills, both my own and enemies I kept a map with the distance to them. Each turn for every ant I knew I updated these maps for the neighborhood of these ants (three steps max). This is off course not the fasted way to create these maps, but it highly efficient in spreading the calculation time. During my calculations I even did not kept a track of what I did, I just managed to do every tile just one time.

For my combat I used a implementation of what was called SimpleCombat at the forums. My first own attempt (without reading the thoughts of others) did not take to notice if a move was possible. Furthermore I was struggling with the results: when was it safe, and when not. Although I had acceptable results, one week before the deadline a started from scratch. The big difference I made, was that I now did take account of where an enemy could go to or not, and the results where given as a change of Safe or Die. Therefore every strategy could have is own risk management. Plans to make these changes better guessed never came to life, but the intentions where there.

Another improvement with my new SimpleCombat was to first look at all attacking moves, then to all staying/defence moves and just at the end check If I should flee. Because staying is safer then attacking, Truusje sometimes did an attack where the first ant started wasn’t supported by its friends. A bit annoying.

As the main goal of the game was razing hills I made a Raze Enemy Hill strategy. It just picked an enemy hill and gave candidate moves for ants to run to it. This was extremely successful against weaker bots, and bots with just an simple static defense of there hills. At the finales (with only the top 200 left) it tended to be too aggressive. I dropped from a stable 50th position to a final ranking of 72.

This was a known issue. Its strength off course is that it is hard to defend against such a lot of ants. A bot has more to do than just stopping you. However, when it managed to stop Truusje (and the good bots did quite often), Truusje sacrificed a lot of ants (in a lot of cases too much). And there was no abort mission implemented, or the opportunity to look for another targer hill for my other ants. I had the idea, but time…

And as a lot of you, I did try strategies that didn’t work. I tried a strategy for multiple hills, where I kept an ant on a half of my hills (when I had 2 hills or more). No good results. I tried a lot of spreading approaches. A lot of them had bad results too. I tried a basic static defense, it didn’t work for me, just as a borrowed symmetry detection didn’t.

I had a more (or less) dynamic hill defense at the end. I made a formation around my hill with a distance to it, depending on the number of ants I’ve got. When there was no danger I only used the even rows, else I used all tiles. Furthermore I ordered ants nearby to run to enemy ants if they were close to my hill. This worked for me well, especially because they were still allowed to run to food if the were close to it.

As almost all competitors I had tons of ideas who needed more time and research. But in the end I’m satisfied with the result. Although the top bots crushed Truusje hard, Truusje itself was doing fine against the big majority. And best of all: I liked its aggressive style, running for the enemy till the bitter ant end…

Code and compiled versions can be found here: www.corniel.nl/download.

Envelope

October 9th, 2011

Het is al weer een week geleden dat De Daler en ik in zomerse omstandigheden op de vroege zondagmorgen met oto’s vol met tijdritmeuk afreisden naar de Vlaardingsedijk te Maassluis. Nog een afsluitend koppeltijdritje, en dan kruis er over. Een tt-seizoen ten einde.

Het was warm edoch behaaglijk, we namen goed over, hielden het tempo er goed in en waren binnen dertien minuten (12:39,81 om precies te zijn) binnen. En keken nog een uur lang naar de overige koppels. We hadden naar huis kunnen gaan, maar het was goed toeven in de zon, en bovenal: wellicht lonkte er het podium.

En het wachten wérd beloond. Een plekje op het podium gras, en een heus envelopje. Toegegeven, de inhoud volstond niet eens om de brandstofkosten te vergoeden, maar toch. Uiteindelijk derde koppel bij het B-garnituur, en zevende op totaal 74 koppels. Goed voor 46,43 kilometer per uur…

Volgens de organisatie dan. Want het parcours was echt geen 9,8 kilometer lang zoals gemeld, maar eerder 9,3 à  9,4 kilometer. En dan kom je op een gemiddelde tussen de 44,0 en 44,5 kilometer per uur. Ook niet slecht natuurlijk.

Kunstgebit

September 29th, 2011

Drie jaar op rij versloeg ik het monster dat huis kan houden in de Flevopolder. Ik raakte er bedreven in. Wat zeg ik, ik raakte verslaafd. Vorig jaar had ik een griepje, want anders had ik da monster natuurlijk weer neergesabeld. Lijkt me logisch…

De logica leerde: beter in vorm dan in 2009, vergelijkbare omstandigheden, dus een p.r. Kind kan de was doen. Gewoon drie uur vijftien blijven trappen. Simpel plan. Enkel nog uitvoeren…

Dus nadat ik de ware MTR-helden, Vincent, Tim en Theo een beetje had bijgestaan, ging ik me opmaken voor drie rondjes. Voor het eerst zonder muziek, voor het eerst met SRM. En met een cammelback. Puntjes op de ï.

Het eerste rondje ging goed. Vermogen op geplande waarde, snelheid op geplande waarde, hartslag op geplande waarde. Lijf: ok. Het drinken ging echter wat minder. Ik kreeg de meegenomen energiedrank nauwelijks weg, en het water leek na te bruisen van de bruistabletten waarmee ik de camelback had schoongemaakt. Ik kreeg het nauwelijks weg en m’n maag protesteerde.

De eerste rondetijd was echter prima, dus vol goede moed begon ik aan m’n tweede rondje. Al begon mijn zitvlak enorm te irriteren, mijn rechtervoet zwol op, en mijn bovenbenen begonnen pijn te doen.  Tempo en vermogen waren nog goed, maar mijn hartslag begon te zakken.

Na zo’n zestig kilometer haalde ik de renner in die voor mij gestart was, dat gaf wat hoop. Sportdrank kreeg ik echter nauwelijks naar binnen en water uit de cammelback werkte ook niet.

De snelheid zakte, de wind nam toe. De hartslag zakte verder, het vermogen zakte dramatisch. Zoef! Daar werd ik weer terug ingehaald. Hoe ik ook probeerde ik hield hem niet meer bij. Ik voelde me leeg. Ik voelde m’n benen. Ik voelde m’n zitvlak. Ik voelde m’n rechtervoet. Vol zelfmedelijden peddelde ik voort. Ik ging rechtop zitten, richting de finish had ik de wind in de rug. M’n snelheid ging nauwelijks omhoog. Did Not Finish.

Het monster had teruggeslagen. Corniel-Monster: 3-2.

Daar sta je dan, langs de kant. Vermoedelijk omdat je je cammelback niet goed hebt uitgespoeld nadat je hem hebt gereinigd met bruistabletten. Die dingen zijn (niet voor niets?) toch vooral bedoeld voor een kunstgebit.

N.B. Het nadeel van een publiek figuur zijn – ookal is het dan binnen de kleine intieme tijdritwereld – is dat velen je aanspreken op tegevallende prestaties, terwijl je het liefst onder een steen zou willen schuilen, en wachten tot het weer over is.

Voetreis

September 12th, 2011

Zaterdag stond na een jaar afwezigheid de tijdrit van Streefkerk weer op het programma. Twee jaar geleden reed ik er goed, nu was ik in vorm, en het leek me wel wat om aan mijn lijst met mooie pr’s dit seizoen nog een of twee toe te voegen.

De start was direct goed. Staand op de pedalen denderde ik de dijk op en halverwege kon ik gaan zitten en verder versnellen. De trapfrequentie lag net rond de 105, zo’n tien hoger dan wat ik de rest van het seizoen gezien had en het liep.

De dijk af zag ik de renner die voor mij was gestart was zienderogen dichterbij komen. Het parcours ging nu over het fietspad (een discissiepunt onder de deelnemers vooraf) en dat was smal en lag bezaaid met kleine steentjes.

In de hoop kleven te voorkomen en aangespoord door een landbouwvoertuig dat mij inhaalde op de naastgelegen weg voerde ik de snelheid op tot ruim zeveneveertig kilometer per uur. Ik ging hem in halen… Pang, bonk, bonk, bonk, bonk, rammel rammel…

Lek.

Voetreis naar Streefkerk

Langzaam daalde het besef in. Ik ging vandaag geen pr rijden, geen podium halen. Het enige wat ik gewonnen had was een voetreis. Eentje terug naar de finish. Streefkerk was nog ver…

File?!

July 11th, 2011

You are not stuck in traffic. You are traffic. Break free. Ride a bike!


Waar van acte.