Archive for the ‘Hello World!’ Category

If programming languages were weapons

Thursday, December 4th, 2014

Debugger display attribute

Wednesday, 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.

[code=C#]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; }
}
}[/code]

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.

[code=C#]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);
}
}
}[/code]

Example of handling cookies Domain-driven

Wednesday, 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.

[code=c#]using System;
using System.Web;

namespace HelloWorld.Web
{
///

Represents a cookie that stores the last division visited by an user (ID).

public class DivisionCodeCookie : WrappedCookie
{
///

The key for the division code.

private const string DivisionCodeKey = “Division”;

///

Initializes a new division code cookie.

/// The user ID. /// The division code. public DivisionCodeCookie(Guid userId, DivisionCode code)
: base(userId)
{
this.Code = code;
}

///

Initializes a new division code cookie.

private DivisionCodeCookie(HttpCookie cookie) : base(cookie) { }

///

Gets and set the division code of the cookie.

public DivisionCode Code
{
get { return DivisionCode.TryParse(UnderlingCookie.Values[DivisionCodeKey]); }
set { UnderlingCookie.Values[DivisionCodeKey] = value.ToString(); }
}

///

Creates a copy of the division code cookie.

public DivisionCodeCookie Copy() { return new DivisionCodeCookie(this.UserId, this.Code); }

///

Casts an HTTP Cookie to a Division code cookie.

///
/// Making the cast implicit allows the use of wrapped cookie when a HTTP cookie is asked.
///

public static implicit operator DivisionCodeCookie(HttpCookie http) { return new DivisionCodeCookie(http); }
}
}[/code]

The base class.

[code=c#]using System;
using System.Diagnostics;
using System.Web;

namespace HelloWorld.Web
{
///

Represents a cookie.

///
/// It is a wrapper that allows to add custom logic to the cookie.
///

[DebuggerDisplay("{DebuggerDisplay}")]
public abstract class WrappedCookie
{
///

Initials a new wrapped cookie based on an HTTP cookie.

protected WrappedCookie(HttpCookie httpCookie)
{
if (httpCookie == null) { throw new ArgumentNullException(“httpCookie”); }
this.UnderlingCookie = httpCookie;
}

///

Initials a new wrapped cookie based on an user ID.

protected WrappedCookie(Guid userId) : this(GetCookieName(userId)) { }

///

Initials a new wrapped cookie based on cookie name.

protected WrappedCookie(string name): this(new HttpCookie(name)){}

///

Gets or set the underlying HTTP cookie.

protected HttpCookie UnderlingCookie { get; set; }

///

Gets or set the user ID of the cookie.

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); }
}

///

Gets or set the name of the cookie.

public string Name
{
get { return UnderlingCookie.Name; }
set{ UnderlingCookie.Name = value;}
}
///

Gets or set the domain of the cookie.

public string Domain
{
get { return UnderlingCookie.Domain; }
set { UnderlingCookie.Domain = value; }
}
///

Gets or set the path of the cookie.

public string Path
{
get { return UnderlingCookie.Path; }
set { UnderlingCookie.Path = value; }
}
///

Gets or set the expiration date of the cookie.

public DateTime Expires
{
get { return UnderlingCookie.Expires; }
set { UnderlingCookie.Expires = value; }
}

///

Gets or set a value that specifies whatever a cookie is accessible by client-side script.

public bool HttpOnly
{
get { return UnderlingCookie.HttpOnly; }
set { UnderlingCookie.HttpOnly = value; }
}
///

Gets or set a value indicating specifies whatever to transmit the cookie Secure Sockets Layers (SSL)–that is, over HTTPS only.

public bool Secure
{
get { return UnderlingCookie.Secure; }
set { UnderlingCookie.Secure = value; }
}
///

Determines whatever the cookie is allowed to participate in output caching.

public bool Shareable
{
get { return UnderlingCookie.Shareable; }
set { UnderlingCookie.Shareable = value; }
}

///

Casts a wrapped cookie (back) to an HTTP cookie.

///
/// Making the cast implicit allows the use of wrapped cookie when an HTTP cookie is asked.
///

public static implicit operator HttpCookie(WrappedCookie wrapped) { return wrapped.UnderlingCookie; }

///

Cleans the cookie up by clearing the value and set the expire date in the past.

public void Cleanup()
{
UnderlingCookie.Expires = DateTime.Now.AddMinutes(-1);
UnderlingCookie.Values.Clear();
}

///

Gets the name for the cookie based on the user ID.

public static string GetCookieName(Guid userId)
{
return string.Format(“ExactServer{0:B}”, userId);
}

///

Gets a debugger display for the wrapped cookie.

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); } }
}
}[/code]

Tech Days 2014 – Shared thoughts

Thursday, 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)

Saturday, 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!

[code=c#]using System;
using System.Collections.Generic;
using System.Text;

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

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

/// <summary>Gets and set the type.</summary>
public InstructionType Type { get; set; }

/// <summary>Gets and set the value.</summary>
public long Value { get; set; }

/// <summary>Gets and set the row.</summary>
public int Row { get; set; }

/// <summary>Gets and set the column.</summary>
public int Col { get; set; }

/// <summary>Gets and set the color.</summary>
public int Color { get; set; }

/// <summary>Gets and set the dirction.</summary>
public DirectionType Direction { get; set; }

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

/// <summary>Gets a hash code.</summary>
public override int GetHashCode()
{
return ToString().GetHashCode();
}

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

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

/// <summary>Equals operator.</summary>
public static bool operator ==(Instruction inst0, Instruction inst1)
{
if (!object.Equals(inst0, null))
{
return inst0.Equals(inst1);
}
return object.Equals(inst1, null);
}
/// <summary>Don’t equals operator.</summary>
public static bool operator !=(Instruction inst0, Instruction inst1)
{
return !(inst0 == inst1);
}

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

string[] tokens = line.Split();

if (tokens.Length &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));
}

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

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

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

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

/// <summary>Helper for parsing instructions.</summary>
private static Dictionary&amp;lt;InstructionType, int&amp;gt; TokenLength = new Dictionary&amp;lt;InstructionType, int&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 },
};
}
}[/code]

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:
[code=c#]using System.Collections.Generic;
using System.Linq;

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

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

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

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

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

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

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

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

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

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

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

/// <summary>Handles the TurnAfterStrategy.&amp;lt;/summary&amp;lt;
/// &amp;lt;remarks&amp;lt;
/// This one is called for an ant that uses this strategy.
/// &amp;lt;/remarks&amp;lt;
public virtual void OnTurnAfterStrategy(AntsLoc oldLoc, AntsLoc newLoc, DirectionType dir, TruusjeCandidateMove move) { }
/// <summary>Handles the TurnAfterStrategy.&amp;lt;/summary&amp;lt;
/// &amp;lt;remarks&amp;lt;
/// This one is called for every ant that moved.
/// &amp;lt;/remarks&amp;lt;
public virtual void OnTurnAfter(AntsLoc oldLoc, AntsLoc newLoc, DirectionType dir, TruusjeCandidateMove move) { }
/// <summary>Handles the TurnFinish.&amp;lt;/summary&amp;lt;
/// &amp;lt;remarks&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;lt;/remarks&amp;lt;
public virtual void OnTurnFinish() { }

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

/// <summary>Creates a candidate move.&amp;lt;/summary&amp;lt;
/// &amp;lt;remarks&amp;lt;
/// Binds to the strategy.
/// &amp;lt;/remarks&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);
}

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

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

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.

Idiots

Monday, August 16th, 2010

Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning (..)

Rick Cook

Windows multi icon

Friday, July 23rd, 2010

Creating a Windows icon containing multiple bitmaps is not supported by .NET. Fortunately, on the net (the other one) I found a (version 1.1) .NET implementation to read a multi icon.

The re-usability wasn’t that great, merging and writing was not supported, and as .NET 1.1 did not support Typed lists yet, I had to rewrite it all:

[code=c#]using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;

namespace HelloWorld.Drawing
{
///

Represents a Windows multi icon, which is a collection of small bitmap images
/// used to represent an object. Icons can be thought of as transparent
/// bitmaps, although their size is determined by the system.
///

public class MultiIcon : List
{
///

Initializes a new instance of the Tjip.Drawing.MultiIcon class.

public MultiIcon() { }

///

Represents the Widows multi icon as System.String.

public override string ToString()
{
var str = string.Format(“{0} Items: {1}”,
GetType().FullName,
this.Count);
if (this.Count == 1)
{
str += string.Format(“, Size: {0} px”,
this.First().Width);
}
if (this.Count > 1)
{
str += string.Format(“, Smallest: {0} px, Largest: {1} px”,
this.GetSmallest().Width,
this.GetLargest().Width);
}
return str;
}

///

Loads a Windows multi icon based on the specfied path.

/// The filepath to load from. public static MultiIcon Load(string filepath)
{
using (var stream = new FileStream(filepath, FileMode.Open, FileAccess.Read))
{
return Load(stream);
}
}
///

Loads a Windows multi icon from a stream.

/// The stream to load from. public static MultiIcon Load(Stream stream)
{
// Read the stream.
using (var reader = new BinaryReader(stream))
{
var icon = new MultiIcon();
var entries = new List();

// Read the header.
var header = reader.ReadMultiIconHeader();

// Read the icon entries.
for (int i = 0; i < header.Count; i++)
{
var entry = reader.ReadMultiIconEntry();
entries.Add(entry);
}
// Read the icons based on the entries.
foreach (var entry in entries)
{
var ico = reader.ReadIcon(header, entry);
icon.Add(ico);
}
return icon;
}
}
}

///

Methods for Windows multi icon, that should be available
/// for all collections of Widows icons.

public static class MultiIconExtensions
{
///

Saves the Windows icons to a single file.

/// The list of icons. /// The filepath to save to. public static void Save(this IList icons, string filepath)
{
using (var stream = new FileStream(filepath, FileMode.CreateNew, FileAccess.Write))
{
icons.Save(stream);
}
}
///

Saves the Windows icons to a single stream.

/// The list of icons. /// The stream to save to. public static void Save(this IList icons, Stream stream)
{
using (var writer = new BinaryWriter(stream))
{
long startposition = stream.Position;

var entries = new List();
var buffers = new List();
// create header.
var header = new MultiIconHeader()
{
Count = (short)icons.Count,
};
// create entries.
for (int i = 0; i < icons.Count; i++)
{
using (var icon_stream = new MemoryStream())
{
var entry = new MultiIconEntry();
var item = icons[i];
item.Save(icon_stream);
icon_stream.Position = 0;

using (var reader = new BinaryReader(icon_stream))
{
// icoHeader.Reserved[2],
// icoHeader.Type[2],
// none.Pos[2],
// Width[1],
// Height[1],
// ColorCount[1],
// Reserved[1]
// Planes[2]
// BitCount[2]
// BytesInRes[4]
// none.OffSet[4]
var header_reserved = reader.ReadInt16();
var header_type = reader.ReadInt16();
var none_startpos = reader.ReadInt16();
entry.Width = reader.ReadByte();
entry.Height = reader.ReadByte();
entry.ColorCount = reader.ReadByte();
entry.Reserved = reader.ReadByte();
entry.Planes = reader.ReadInt16();
entry.BitCount = reader.ReadInt16();
entry.BytesInRes = reader.ReadInt32();
entry.ImageOffset = MultiIconHeader.ByteSize + MultiIconEntry.ByteSize * icons.Count + buffers.Sum(buf => buf.Length);
var none_offset = reader.ReadInt32();

var buffer = new byte[icon_stream.Length - icon_stream.Position];
icon_stream.Read(buffer, 0, buffer.Length);
entries.Add(entry);
buffers.Add(buffer);
}
}
}
// Writer header.
writer.Write(header);

// Write entries.
foreach (var entry in entries)
{
writer.Write(entry);
}
// Write images.
foreach (var buffer in buffers)
{
writer.Write(buffer);
}
// Clear buffer and save.
writer.Flush();
}
}

///

Gets the smallest icon.

/// The list of icons. /// The smallest icon.
public static Icon GetSmallest(this IEnumerable icons)
{
var icon =
(
from
item in icons
orderby
item.Width ascending
select
item
)
.FirstOrDefault();

return icon;
}
///

Gets the largest icon.

/// The list of icons. /// The largest icon.
public static Icon GetLargest(this IEnumerable icons)
{
var icon =
(
from
item in icons
orderby
item.Width descending
select
item
)
.FirstOrDefault();

return icon;
}
}
///

Represents the header of a Windows (multi) icon.

internal class MultiIconHeader
{
///

The byte size of a single icon header.

public const int ByteSize = 2 + 2 + 2;

///

Initializes a new instance of the Tjip.Drawing.MultiIconHeader class.

public MultiIconHeader()
{
this.Type = 1;
}

public short Reserved { get; set; }
public short Type { get; set; }
public short Count { get; set; }
}

///

Extension methods for MultiIconHeader.

internal static class MultiIconHeaderExtensions
{
///

Reads a Windows (multi) icon header from the current stream
/// and advances the current position of the stream by six bytes.
///

/// The reader. public static MultiIconHeader ReadMultiIconHeader(this BinaryReader reader)
{
var header = new MultiIconHeader()
{
Reserved = reader.ReadInt16(),
Type = reader.ReadInt16(),
Count = reader.ReadInt16(),
};
return header;
}

///

Writes a Windows (multi) icon header to the current stream
/// and advances the current position of the stream by six bytes.
///

/// The writer. /// The Windows multi icon header to write. public static void Write(this BinaryWriter writer, MultiIconHeader header)
{
writer.Write(header.Reserved);
writer.Write(header.Type);
writer.Write(header.Count);
}
}
///

Represents the entry of a Windows (multi) icon.

internal class MultiIconEntry
{
///

The byte size of a single icon entry.

public const int ByteSize = 1 + 1 + 1 + 1 + 2 + 2 + 4 + 4;

///

Initializes a new instance of the Tjip.Drawing.MultiIconEntry class.

public MultiIconEntry() { }

public byte Width { get; set; }
public byte Height { get; set; }
public byte ColorCount { get; set; }
public byte Reserved { get; set; }
public short Planes { get; set; }
public short BitCount { get; set; }
public int BytesInRes { get; set; }
public int ImageOffset { get; set; }
}

///

Extension methods for MultiIconEntry.

internal static class MultiIconEntryrExtensions
{
///

Reads a Windows (multi) icon entry from the current stream
/// and advances the current position of the stream by sixteen bytes.
///

/// The reader. public static MultiIconEntry ReadMultiIconEntry(this BinaryReader reader)
{
var entry = new MultiIconEntry()
{
Width = reader.ReadByte(),
Height = reader.ReadByte(),
ColorCount = reader.ReadByte(),
Reserved = reader.ReadByte(),
Planes = reader.ReadInt16(),
BitCount = reader.ReadInt16(),
BytesInRes = reader.ReadInt32(),
ImageOffset = reader.ReadInt32(),
};
return entry;
}

///

Reads a Windows icon from the current stream
/// and advances the current position of the stream to the end of the
/// Windows icon in the stream.
///

/// The reader. public static Icon ReadIcon(this BinaryReader reader, MultiIconHeader header, MultiIconEntry entry)
{
const short ICON_STREAM_START = 1;
const int ICON_STREAM_OFFSET = 22;

using (var newIcon = new MemoryStream())
{
using (var writer = new BinaryWriter(newIcon))
{
// Write it
writer.Write(header.Reserved);
writer.Write(header.Type);
writer.Write(ICON_STREAM_START);
writer.Write(entry.Width);
writer.Write(entry.Height);
writer.Write(entry.ColorCount);
writer.Write(entry.Reserved);
writer.Write(entry.Planes);
writer.Write(entry.BitCount);
writer.Write(entry.BytesInRes);
writer.Write(ICON_STREAM_OFFSET);

// Grab the icon
byte[] tmpBuffer = new byte[entry.BytesInRes];
reader.BaseStream.Position = entry.ImageOffset;
reader.Read(tmpBuffer, 0, entry.BytesInRes);
writer.Write(tmpBuffer);

// Finish up
writer.Flush();
newIcon.Position = 0;
return new Icon(newIcon);
}
}
}

///

Writes a Windows (multi) icon entry to the current stream
/// and advances the current position of the stream by sixteen bytes.
///

/// The writer. /// The Windows multi icon entry to write. public static void Write(this BinaryWriter writer, MultiIconEntry entry)
{
writer.Write(entry.Width);
writer.Write(entry.Height);
writer.Write(entry.ColorCount);
writer.Write(entry.Reserved);
writer.Write(entry.Planes);
writer.Write(entry.BitCount);
writer.Write(entry.BytesInRes);
writer.Write(entry.ImageOffset);
}
}
}[/code]

It works perfectly. The only thing I still need to solve is the conversion of PNG24 to ICO:
[code=c#]var icon = Icon.FromHandle(new Bitmap("some_pgn24.png").GetHicon());[/code]
This results in an icon that does not have an alpha-channel. When I solved this, I will inform you.

ISO 8601

Friday, December 18th, 2009

Probably you are familiar with ISO. Some guys in a big office creating tons of paperwork every year. One of the standards they created is 8601. One of the items it describes is the number of the week, witch is commonly used.

Unfortunately Microsoft didn’t add an implementation for this week number in its .NET framework. A colleague of mine found one and added it to our extensions:

[code=c#]namespace HelloWorld.Extensions
{
public static class DateTimeExtensions
{
/// Returns ISO WeekNumber (1-53) for a given year.
///
The datetime. public static int ISOWeekNumberOld(this System.DateTime dt)
{
// Set Year
int yyyy = dt.Year;
// Set Month
int mm = dt.Month;
// Set Day
int dd = dt.Day;
// Declare other required variables
int DayOfYearNumber;
int Jan1WeekDay;
int WeekNumber = 0, WeekDay;
int i, j, k, l, m, n;
int[] Mnth = new int[12] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
int YearNumber;
// Set DayofYear Number for yyyy mm dd
DayOfYearNumber = dd + Mnth[mm - 1];
// Increase of Dayof Year Number by 1, if year is leapyear and month is february
if ((DateTime.IsLeapYear(yyyy) == true) && (mm == 2))
DayOfYearNumber += 1;
// Find the Jan1WeekDay for year
i = (yyyy – 1) % 100;
j = (yyyy – 1) – i;
k = i + i / 4;
Jan1WeekDay = 1 + (((((j / 100) % 4) * 5) + k) % 7);
// Calcuate the WeekDay for the given date
l = DayOfYearNumber + (Jan1WeekDay – 1);
WeekDay = 1 + ((l – 1) % 7);
// Find if the date falls in YearNumber set WeekNumber to 52 or 53
if ((DayOfYearNumber <= (8 – Jan1WeekDay)) && (Jan1WeekDay > 4))
{
YearNumber = yyyy – 1;
if ((Jan1WeekDay == 5) || ((Jan1WeekDay == 6) && (Jan1WeekDay > 4)))
WeekNumber = 53;
else
WeekNumber = 52;
}
else
YearNumber = yyyy;
// Set WeekNumber to 1 to 53 if date falls in YearNumber
if (YearNumber == yyyy)
{
if (DateTime.IsLeapYear(yyyy) == true)
m = 366;
else
m = 365;
if ((m – DayOfYearNumber) < (4 – WeekDay)) { YearNumber = yyyy + 1; WeekNumber = 1; } } if (YearNumber == yyyy) { n = DayOfYearNumber + (7 – WeekDay) + (Jan1WeekDay – 1); WeekNumber = n / 7; if (Jan1WeekDay > 4)
WeekNumber -= 1;
}
return (WeekNumber);
}
}
}
[/code]

Not only is this an extreme unreadable piece of code, it seems to be invalid as well. So I decided to create something more understandable:

[code=c#]namespace HelloWorld
{
/// Represents a date as specified by ISO 8601 week date.
///
/// See: http://en.wikipedia.org/wiki/ISO_8601
/// and: http://en.wikipedia.org/wiki/ISO_week_date
///
public struct Iso8601WeekDate
{
private int m_Day;
private int m_Year;
private int m_Week;
private DateTime m_Date;

/// Initializes a new instance of the Tjip.Iso8601WeekDate structure to the specified System.DateTime.
///
The date of the ISO 8601 WeekDate. public Iso8601WeekDate(DateTime date)
{
// Only the date will be available.
m_Date = date.Date;
// Set the year.
m_Year = date.Year;
// The day is oke by default, Unless its sunday (int value = 0)…
m_Day = (date.DayOfWeek == DayOfWeek.Sunday) ? 7 : (int)date.DayOfWeek;

// Now the week number.
DateTime startdate = GetFirstDayOfFirtWeekOfYear(date.Year);
DateTime enddate = GetFirstDayOfFirtWeekOfYear(date.Year + 1);
// The date is member of a week in the next year.
if (m_Date >= enddate)
{
startdate = enddate;
m_Year++;
}
// The date is member of a week in the previous year.
if (m_Date < startdate)
{
startdate = GetFirstDayOfFirtWeekOfYear(date.Year – 1);
m_Year–;
}
// Day of the week.
int dayofyear = (m_Date – startdate).Days;

// The week number is not zero based.
m_Week = dayofyear / 7 + 1;
}

/// Gets the date component of this instance.
public DateTime Date { get { return m_Date; } }
/// Gets the year component of the date represented by this instance.
public int Year { get { return m_Year; } }
/// Gets the week component of the date represented by this instance.
public int Week { get { return m_Week; } }
/// Gets the day component of the date represented by this instance.
public int Day { get { return m_Day; } }

/// Gets the date of the first day of the first week of the year.
///
/// Source: http://en.wikipedia.org/wiki/ISO_8601
///
/// There are mutually equivalent descriptions of week 01:
/// – the week with the year’s first Thursday in it (the formal ISO definition),
/// – the week with 4 January in it,
/// – the first week with the majority (four or more) of its days in the starting year,
/// – the week starting with the Monday in the period 29 December – 4 January.
///
public static DateTime GetFirstDayOfFirtWeekOfYear(int year)
{
DateTime start = new DateTime(year, 01, 04);
while (start.DayOfWeek != DayOfWeek.Monday)
{
start = start.AddDays(-1);
}
return start;
}

/// Represents the Tjip.Iso8601WeekDate as System.String.
public override string ToString()
{
return ToString(“YYYY-Www-D”);
}
/// Represents the Tjip.Iso8601WeekDate as System.String.
///
The format. ///
/// Representations of the following formatting are allowed:
/// – YYYYWww
/// – YYYY-Www
/// – YYYYWwwD
/// – YYYY-Www-D
///
/// [YYYY] indicates the ISO week-numbering year which is slightly different
/// to the calendar year (see below).
///
/// [Www] is the week number prefixed by the letter ‘W’, from W01 through W53.
///
/// [D] is the weekday number, from 1 through 7, beginning with
///
/// Monday and ending with Sunday. This form is popular in the
/// manufacturing industries.
///
public string ToString(string format)
{
switch (format)
{
case “YYYYWww”: return string.Format(“{0}W{1:00}”, this.Year, this.Week, this.Day);
case “YYYY-Www”: return string.Format(“{0}-W{1:00}”, this.Year, this.Week, this.Day);
case “YYYYWwwD”: return string.Format(“{0}W{1:00}{2}”, this.Year, this.Week, this.Day);
case “YYYY-Www-D”: return string.Format(“{0}-W{1:00}-{2}”, this.Year, this.Week, this.Day);
default: throw new NotSupportedException(string.Format(“The format ‘{0}’ is not supported.”, format));
}
}
/// Returns the hash code for this instance.
///
/// A 32-bit signed integer that is the hash code for this instance.
///
public override int GetHashCode()
{
// It should be fast, so shift.
// ..3…..6…………14 = 23 bit
// bits: DDDWWWWWWYYYYYYYYYYYYYY
int hash = this.Day + this.Week >> 3 + this.Year >> 9;
return hash;
}
}
}[/code]

And to extend it to System.DateTime:

[code=c#]namespace HelloWorld.Extensions
{
public static class DateTimeExtensions
{
public static Iso8601WeekDate ToIso8601WeekDate(this DateTime dt)
{
return new Iso8601WeekDate(dt);
}
/// Returns the (ISO 8601) number of the week.
///
The datetime. /// The (ISO 8601) number of the week.
///
/// Source: http://en.wikipedia.org/wiki/ISO_8601
///
public static int ISO8601WeekNumber(this DateTime dt)
{
Iso8601WeekDate weekdate = new Iso8601WeekDate(dt);
return weekdate.Week;
}
}
}[/code]

Not only is this imho much easier to read (and working correctly), you can format it like ISO spected it.

WTF (3)

Monday, November 30th, 2009

Well, no comment required, i guess.

[code=c#](..)
if (contextvalue == 0)
{
string empty = “”;
return (empty.ToString());
}[/code]

Roman numerals

Thursday, August 20th, 2009

Sometimes, you write a piece of code you’re proud of. Because it’s a clever, elegant, or just fun. The snippet I post here is a bit of all, at least in my opinion.

[code=c#]///

Converts an System.Int32 to Roman number strings.
/// http://en.wikipedia.org/wiki/Roman_numerals
///

/// Number to convert. /// Formatted Roman number.
public static string ToFormattedRomanNumber(int number)
{
if (number <= 0 || number > 3999)
{
throw new ArgumentOutOfRangeException(“With the default ASCII set only Roman numerals between 1 and 4000 can be represented.”);
}
// 1, 5, 10, 50, 100, 500, 1000.
string[] order = { “I”, “V”, “X”, “L”, “C”, “D”, “M” };
int pointer = 0;
int digits = number;
string result = string.Empty;

while (digits > 0)
{
int digit = digits % 10;
switch (digit)
{
case 0: break;
case 1: result = string.Format(
“{1}{0}”, result, order[pointer]);
break;
case 2: result = string.Format(
“{1}{1}{0}”, result, order[pointer]);
break;
case 3: result = string.Format(
“{1}{1}{1}{0}”, result, order[pointer]);
break;
case 4: result = string.Format(
“{1}{2}{0}”, result, order[pointer], order[pointer + 1]);
break;
case 5: result = string.Format(
“{1}{0}”, result, order[pointer + 1]);
break;
case 6: result = string.Format(
“{2}{1}{0}”, result, order[pointer], order[pointer + 1]);
break;
case 7: result = string.Format(
“{2}{1}{1}{0}”, result, order[pointer], order[pointer + 1]);
break;
case 8: result = string.Format(
“{2}{1}{1}{1}{0}”, result, order[pointer], order[pointer + 1]);
break;
case 9: result = string.Format(
“{1}{2}{0}”, result, order[pointer], order[pointer + 2]);
break;
}
digits = digits / 10;
pointer = pointer + 2;
}
return result;
}[/code]