Showing posts with label DotNet. Show all posts
Showing posts with label DotNet. Show all posts

Sunday, August 2, 2009

New Tool: Convert .FB2 files to PDF

ereader

It all started a couple of months ago when my wife, @othraen, got her Sony eReader. She loves it, and reads a couple of books a week on it. I can definitely see why she likes it – it’s slick, simple, and very pleasant to read on. Great toy, if you like books!

Anyway, with the speed she’s reading, not even the 5000000000000000 (or whatever count they reached now) free titles at Google Books is enough, and she’s started looking online for public domain Russian language books (most Russian is like Greek to me, but she likes it :-) ). As it turns out most of the Russian e-books online are in the FictionBook 2 format (.FB2). It’s an open XML format, probably defined by a league of Russian e-book publishers or so – it doesn’t seem to be all that popular in the rest of the world.

As you might imagine, the friendly people at Sony didn’t take this into consideration when planning on which formats to use in the eReader, so it is not supported. However, our friend, good old reliable PDFs are of course supported. It didn’t take many Google searches to find the fb2pdf.com website – a site devoted to converting fb2 files to PDF. It worked great on the first book….And on the second book. But later on, several of the books she tried to convert wouldn’t work. It would just freeze up and we could wait for ever and ever. As it so often happens, this resulted in the statement I fear so much “Honey, can’t you spend a little less time playing around with that programming stuff and a little more helping me get this book I want to read on my ereader??!” (yes, one of those questions where there’s only one right answer). In this case it would have made sense to simply drop a mail to the owners of the before mentioned website, asking them to fix whatever bug that caused the conversion to fail – but I was determined not to let this be the first time I give in to common sense :-)

image Instead this was a perfect opportunity to take a closer look at FB2, PDFs, the iTextSharp library, SharpZipLib and ClickOnce deployment. At the same time, I’d get to program in my spare time – and with a perfect excuse – helping her :-)

It didn’t take much more than a couple of hours before I had a working windows application, that can load .fb2 files (or zip-files containing 1 .fb2 file), correct / change their Author / Title (since the ereader doesn’t seem to support Cyrillic characters in the title browsing menu), and convert into a PDF. It’s a ClickOnce application, which means you can install it directly from it’s online source – and it will automatically get updated if I upload a new version. The only tricky thing in developing it was actually embedding a font to show the Cyrillic Unicode characters that most Russian books are comprised of.

[UPDATE 2009-12-20] I finally found some time to fix a few bugs and add two new check-boxes: "Optimize for Sony Reader" and "Optimize for Kindle". Right now, all they do is to optimize page-sizes, so it should work better on the respective devices - but in the future they might also adjust font, font-sizes, etc. Let me know how well it works - I don't have a kindle, so I can't test it myself. Update should install itself.

Install the FB2 to PDF Converter tool from here.

Sunday, January 4, 2009

WikiX - a Wiki for EPiServer CMS

Just before Christmas I was part of a team that launched WikiX an Open Source Wiki for EPiServer CMS 5 R2. Even though it's still in beta, it is actually really nice with a lot of cool features. WikiX is based on some of the thoughts and ideas presented here: http://allantech.blogspot.com/2008/10/wiki-vs-cms-difference-is-psychological.html.

wikix

See the announcement: http://labs.episerver.com/en/Blogs/Allan/Dates/112230/12/Announcement-WikiX-is-here/

Try it live here: http://wiki.demo.episerver.com/

Download it from Codeplex:http://www.codeplex.com/EPiServerWiki

Tuesday, July 17, 2007

WCF: Sharing Types between Server and Client

A discussion I've run into time and time again through the last few months when I've been working with WCF is whether to use the generated proxy classes client-side or think of something else (like inherting the proxy-classes, creating your own proxies, or somehow try to make the proxy classes identical to the source classes).
I guess the discussions arise as a result of people not being sure if they should consider WCF like Web Services which has a loose coupling (generated client proxies) or like Remoting which often has a tighter coupling between the interacting participants (shared dll).
Until now I've mainly been a fan of the loose coupling because of three things:
  • It's the easiest and fastest just click the right buttons in your VS and you're set! (yes, I can be quite lazy at times but remember that lazy developers often are the best)
  • There's no dependency of a specific version dll between the client and the server. If the Server gets an update that breaks the service-convention the client just has to regenerate it's proxy and you're set.
  • I havn't seen a clean and pretty alternate solution before. Mostly it's been messy.
However today I just came across this excellent codeproject posting that gives a fine example of how to share a type between a WCF Service and a WCF consumer. It turns out that it's built into the SvcUtil (the client proxy generator) as a command-line switch. Now doesn't that make me feel stupid :-)

Friday, July 13, 2007

Automatic Language Detection

A classical task when dealing with textual information is to automatically identify which language a text is written in (no, geeks - it's not a question of VB or C# - I mean human languages!).
Here's my attempt at a very simple, yet useful approach: character-bigram statistics.
I've basically made some extensive statistics on several languages on the frequency of all bigrams, and using that it's now possible to determine which language a given text resembles the most.
Try out my Language Detector here!

The text-corpus I used was another classic, the proceedings of the European Parlament through several years (can be found here).

My first step was to construct a class to contain bigram statistics for some text (LangStat).
In the class I also included code to determine the euclidean distance between two sets of bigram statistics (useful when trying to determine which language a text is most similar to). I implemented it as an operator overload for "-", so you can always determine the distance between two bigram-statistics by simply subtracting them from each other.



//Calculates euclidean distance between two LangStat's
public static double operator -(LangStat a,LangStat c)
{
//Operator overload
double tot = 0;
foreach (Bigram b in a.Bigrams.Keys)
{
if (c.Bigrams.ContainsKey(b))
{
//Bigram exist in remote
double me = (double)a.Bigrams[b] / a.Count;
double them = (double)c.Bigrams[b] / c.Count;
tot += Math.Pow(Math.Abs(me - them), 2);
}
}
return Math.Sqrt(tot);
}


Then I build a Console trainer application, that is able to load the corpus text files for a given language, clean up any unwanted tags in them and then adds the text to a bigram statistic.

When it's done, it use the System.CodeDom to generate source-code for a class that inherits the LangStat, but which is specific to the current language. That way I'll have my languages precompiled and ready to be compared to custom textual content.
This might not be the most efficient approach, but it sure was funny to play around with CodeDom (an interesting namespace that I get to use far to seldom).



static void Main(string[] args)
{
string lang = "sv";
string langname = "Swedish";
string[] files = Directory.GetFiles((...language folder...));

//Build language statistics from file-corpus
LangStat l=new LangStat();
foreach(string f in files){
Console.WriteLine("Examining file: "+f);
StreamReader sr=new StreamReader(f);
string s=sr.ReadToEnd();
sr.Close();
//File loaded
s=Regex.Replace(s,"<[^>]*>"," ",RegexOptions.Multiline);
//Tags removed
l.AddText(s);
}


//Generate Code
System.CodeDom.CodeNamespace ns =
new System.CodeDom.CodeNamespace("Allan.Language.Detection");
CodeTypeDeclaration tp = new CodeTypeDeclaration(langname);
tp.BaseTypes.Add(typeof(LangStat));
tp.IsClass = true;
ns.Types.Add(tp);
CodeConstructor cc = new CodeConstructor();
cc.Attributes = MemberAttributes.Public;
tp.Members.Add(cc);
cc.BaseConstructorArgs.Add(
new CodePrimitiveExpression(l.Bigrams.Count));
foreach (Bigram b in l.Bigrams.Keys)
{
//Could be done much nicer, but I'm in a hurry
cc.Statements.Add(
new CodeSnippetExpression(
"_bigrams.Add(new Bigram('"+b.A+"','"+b.B+"'),"+
l.Bigrams[b].ToString()+")"));
}
cc.Statements.Add(
new CodeAssignStatement(
new CodeVariableReferenceExpression("_count"),
new CodePrimitiveExpression(l.Count)));
System.CodeDom.Compiler.ICodeGenerator gen =
new CSharpCodeProvider().CreateGenerator();
StreamWriter sw=File.CreateText(langname+".cs");
gen.GenerateCodeFromNamespace(ns, sw,
new System.CodeDom.Compiler.CodeGeneratorOptions());
sw.Close();

}


Finally I just had to build a simple windows testing app, that will compare the text written to the languages. Download the solution here.

Friday, July 6, 2007

Code Challenge Results: No luck for the Hash-Party

So far there hasn't been a lot of entries to the latest Code Challenge so I suppose I might have overestimated the abilities of you, my honorable readers.
In fact, the only entry I received was from Peter Thygesen and he admits to actually just having adopted an algorithm by Paul Hsieh.

However just for the fun I compared it to the build-in string hashing algorithm (.GetHashCode()).
The comparison I did was fairly simple: I took 1.000.000 fairly random unique strings (well - actually Guids as strings) and timed how long time it cumulative took to run the algorithms. I also checked how many duplicate hash-codes each algorithm resulted in.
It turns out they were pretty equal.
The build-in algorithm had 114 duplicate hash-codes and took 15275 ms. while Mr. Thygesens entry had 115 duplicates and took 15318 ms.

Thanks for playing, Peter - but I think we have to declare this a no-win :-)

EPiServer 5 CMS - First impressions

A couple of weeks ago I wanted to check out how the new EPiServer 5 looked, so I downloaded a free trial version of the RC2.
It comes in two flavors. There's the traditional installer that installs the Manager which allows you to setup new EPiServer websites with a default look & feel, but on top of that there's also a new Visual Studio integration available that I instantly knew I just had to try out.

The install itself was very (!) easy and without any problems or hickups I had a lot of new features in my visual studio.
For instance I now had the possibility of creating a new EPiServer Project which I instantly did.
This template created blank episerver website, db, etc. for me ready to use.
It's really clear to see that with this new release the clever guys at EPiServer has been focussing a lot on improving the quality of life for all the developers out there who use it as an every day tool to make websites.
At the same EPiServer is now even tighter coupled with the newest Microsoft technologies, basing their CMS on standard ASP.NET 2.0 things like Master pages and ASP.NET User/Role configuration. They've also done a tremendous job of integrationg Workflow Foundation into the core functionality - and to this date this seems like one of the best usages of WWF I've seen so far.

Seen from a developer perspective the new SDK makes me think of EPiServer as a huge toolbox that gives me a lot of tools to efficiently create cool websites and webfunctionality in a standard ASP.NET way, while taking care of a lot of the tedious details. But from an Editor / Administrator perspective you still get the well-known intuitive webbased interface for administrering and editing the website. Cool.


The editor and administrator interface hasn't changed all that much since last version and the entrypoint is still the "famous" right-click menu for logged-in editors. It seems to me like the Editor interface hasn't gotten all that much work done except for a paint-job and perhaps some improved versioning/comparison features (however I could be mistaking, having never been a real-life editor :-) ). Thats okay, though. Rome wasn't build in a day and I certainly prefer the improved SDK and architectural changes.
Yes, I am the kind of guy who cares more about whats under the hood of my car, than the color, shape and sexiness of it's exterior. However it still wouldn't hurt to give a bit of attention to improve the (already good) usability for editors and administrators in a future version. Perhaps AJAX is a good approach here.

While I'm at it, here's another few things for my wishlist for future versions: WCF support for easier data / functionality access and a couple of nice fully-featured demo-sites / templates for the SDK. It could be nice to a couple of ready-to-go samples as VS Templates.

All-in-all I'm very impressed with the RC2 version of EPiServer 5 and I can't wait to play around with it some more. Don't be surprised if a couple of modules start appearing on this blog for free download in the near future. EPiServer continues to be a powerful workhorse in the CMS world, not as flashy and shiny as some competitors but intuitive, strong and flexible.

Thursday, June 28, 2007

Code Challenge: "John the courier"

Yet another brillian idea popped into my mind today: Why not celebrate the rainy summer with a nice indoor competition - a Code Challenge!


Through the next couple of weeks I intend to publish a couple of challenges like the one below.

Think fast, solve the problem and post it as a comment!

The various challenges will have different winning criterias. These could be: "First valid solution posted", "Valid solution with fewest code-lines", "Funniest approach", "Best Performance", etc.

The Winner will win ... well... the honour along with mocking rights over all other coders in the world (at least those who read this blog and who didn't win).

We'll start off with an easy one...



Challenge #1 "John the courier"

In John's little world there is n cities, named by numbers starting at 0. In every city there is a parcel that's supposed to go to another city in John's world.

John live in City no. 0 and starts by picking up a package there. Then, whenever he delivers a package in a city he takes the package from that city and takes it to where it should go.

The distance between the cities is oddly enough the same as the difference in their names (e.g. the distance between city 10 and city 7 is 10-7=3).

John starts off his day with receiving a list of where the parcels in each city should be delivered. Now, John wonders: How far will I have to travel before I get back to my home (City 0).

Suggest a method that takes an int-array where the city is the index and the value is the destination of the package in the city (like this: int CalculateDistanceToHome(int[] CityPackages);) that returns the distance John must travel before he gets home. You can assume that the packages are distributed in such a way that John will always eventually get home.
First valid solution posted is the winner. Bonus points for recursive solutions.
Let the games begin!

Thursday, June 21, 2007

Making cross-thread calls / events

Many developers first introduction to multithreaded programming is the classic challenge of using one or more "background-worker" threads to do some work thats expected to take longer than the average user wants to wait for his windows application to become responsive again.
Making a worker-method and starting up a thread to run it - or asking the ThreadPool to assign the method to a thread from the pool (QueueUserWorkItem) is quite simple - but soon after the coding starts to get interesting (and fun!).

Now you have to worry about using Mutex and Monitor, etc. to ensure that there's no sharing violation between ressource that the threads share. This in itself is a worthy topics of several books and many blogposts (a lot better than my humble abilities allow me to write).
In a simple scenario as described above you might be able to avoid many of these problems if you contain all the necessary data within each worker thread - but judging on the number of times I've been asked about this, you still encounter yet another issue: cross-thread communication.
Imagine that you've started up your worker-thread and it works happily, enjoying as many cpu-cycles as your operating system allows it, while still letting the main application thread provide a responsive UI. Then you'll at some point start to wonder "Okay....so now my fingamaboob is doing some work...thats nice...I wonder how far it's gotten".
- "No problem", I hear you say. "I'll just have my worker thread output it's status to the window running in the main thread."
This approach will often lead to one of two scenarios:


  1. InvalidOperationException, Cross-thread operation not valid

  2. Some weird construction with a shared status variable, that the UI is polling every X seconds

The correct solution to this problem is to use a proper cross-thread call. For instance you can use Invoke (or BeginInvoke if you're the asynchroneous type). All Windows Forms controls has an Invoke method that you can call and provide with a delegate and a set of parameters. That way you are instructing the thread that "owns" the control to run the call the delegate with the specified parameters.

As you can see below it can be done quite elegantly using anonymous methods and a custom delegate.


public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

}



private delegate void ReportStatusHandler(string status);



private void DoBoringWork(object param)

{

for (int i = 0; i < 10000; i++)

{

//Simulate boring work

Thread.Sleep(10);

if (0 == (i % 100))

{

//Output status for every 100



//WRONG:

//listBox1.Items.Add(i.ToString()+" items processed");



//Right:

ReportStatusHandler rsh = new ReportStatusHandler(

delegate(string s) {

listBox1.Items.Add(s);

});

listBox1.Invoke(rsh,

i.ToString() + " items processed");

}

}

}



private void button1_Click(object sender, EventArgs e)

{

//Put work in Queue to be done by ThreadPool

ThreadPool.QueueUserWorkItem(

new WaitCallback(DoBoringWork), null);

}

}

Tuesday, May 29, 2007

WCF: Duplex is awesome!

One of my first assignments in the new job has been to lookup into a couple of the fun new features in .NET 3.0, like WCF and WWF.

So far, I've found Communication Foundation really interesting, albeit a bit annoying to work with.
It's main force is it's flexibility. Where you would usually have to decide on either building a Web Service, your own custom coded TCP Server or use remoting, you can now just build a standard service and then just put in the configuration file which protocol it should use (like HTTP, TCP, Named Pipes, MSMQ, etc) - or well, at least thats the theory. The downside to this is of course that since there's a lot of stuff thats configurable, you really need to understand all the configuration concepts (like Bindings, MetadataExchange, Endpoints, Security, Contracts, etc) properly and configure it well in order to use it.
Another potential problem could be the performance of this communication since all the communication is handled using SOAP (which means that there's a lot of XML serialization and deserialization going on).

It would also seem that a couple of the problems known from WebServices has been addressed. For instance you now no longer need to put the webservices in IIS in order to use web-services - they'll just open a HTTP port for you and act as their own server. It also looks like error-handling has been improved and it looks like there's now some cross-service exception-handling (although not perfect. An ApplicationException thrown from the server will appear to be a "FaultException" on the client - but perhaps I'm missing something here).

The most awesome feature I've stumbled across until now in WCF is the possibility to make Duplex services, e.g. services that are able to initiate communication with the client.
Sure, you could yourself make each client a service as well as a client and then let them exchange connection information, but now this functionality is build into the communications framework.
Naturally this requires some coding/configuration inconviniences, but once they are done it's easy to implement a state-of-the-art Observer pattern across various machines.
Setup a service, allow multiple clients to call the service to register themself as subscribers to various events, and then let the Service notify them when the event occurs.
Jeff Barnes has put a great article on Codeproject with an example of this.

Wednesday, May 16, 2007

Playing around with Embedded Objects in IE

Yesterday I felt like playing around a bit with embedded objects in IE - you know, showing Windows Forms in a browser. An old .NET trick that I've used a couple of times.

For some reason it has never quite become the market standard it was supposed to (probably because it's ugly, inefficient and very browser specific) - but it would have been nice with a good alternative to java applets!

I made a quick adaptation of the SudokuSolver from my previus post to see how it would work as an embedded object. This is what I did:
  1. Made a Windows Forms Library project
  2. Made a Windows Forms User Control
  3. Moved the UI from the Sudoku application to the new User Control (as well as the Code Behind)
  4. Compiled and put on a web-server
  5. Made a HTML Page that includes it as an embedded object, and put the page on the same webserver (this is important)
This is the HTML I used, notice how you provide it with the URL to the Windows Forms DLL, and then the full path (including namespace) to the control to display:


<object id="SudokuControl" height="240" width="206"
classid="http://www.thraen.dk/Download/SudokuWinLib.dll#SudokuWinLib.Sudoku">
</object>


If you are watching this blog in IE, and you have .NET 2.0 installed, and your security settings is just right, there is a chance that you might actually see the Sudoku Solver here:


Thursday, April 26, 2007

Awesome IKVM; Interact with JAVA from .NET

Yet again I've java in my studies at ITU. The current assignment Peter, Thomas and I are working on is to implement an assistant that helps a user solve the classic N-Queens problem using Reduced ordered Binary Decision Diagrams (RoBDDs or simply BDDs).
There's many(!) ways to solve that problem, but using BDDs does seem like a very intriguing approach. The only problem: it requires a BDD engine. We could of course write our own (and actually I'm currently working on that), but in the assignment we were given, there was actually a fully functioning BDD library ready for us to use. Only, it was in java... (NOTE: I don't have any problem with java and I'm not religious in any ways, but usually .NET is my weapon of choice).
"No problem, we'll just use J# to handle it like last time" was the initial reaction.
But, alas, the library was already a compiled jar, no source included. Naturally we could get all the source from sourceforge and port it to J#, but the time seemed right to try a new clever approach!
Luckily Peter found the right solution: Enter IKVM! IKVM is a great set of tools to interact between java and .NET and it works like a charm.
The two main tools is a command-line program that allows you to run compiled java files in .NET instead of java's virtual machine. The other tool that proved to be really useful to us, will allow you to take a JAR and transform it into a .NET DLL.
All I had to do was to call it command-line with the name of the JAR file and the name of the .NET output file, and run it - and in no time I had a working .NET dll that I could reference directly in my .NET projects.
In order for the referencing programs to work though, it's important to have two of the IKVM dlls' included in the "bin" folder or in the GAC (namely the "IKVM.GNU.ClassPath.dll" and "IKVM.Runtime.dll").
Great work, IKVM guys. Keep doing your magic!

And the programming assignement? Well, here is how far we've gotten so far. Keep in mind that it's work in progress. After the hand-in deadline I'll make a new post about how we did it.
I'm also considering trying out other of the known approaches to solve the same problem and comparing them. Drop a comment if you'd be interested in knowning what works best :-)

Monday, April 16, 2007

DPLL in C# - Satisfying problems in CNF

Time for another AI post! These last couple of weeks I've been working with two fellow C# guru's, Peter Thygesen and Thomas Gravgaard on an assignment in our AI class, on implementing a couple of specific parts of the DPLL algorithm, such as the methods for choosing split symbols, finding unit clauses and identifying pure symbols.
"What's DPLL good for?" I hear you cry...Well, it's simple really - or actually it isn't all that simple but I'll try to explain it anyway.Suppose you have a boolean statement in CNF (conjunctive normal form) and you want to test if it's satisfiable, that is - if a certain configuration exist, that will make it true - then you can run the DPLL algorithm to find out. The DPLL basically searches the solution space, but during the search uses the unit-clauses and pure symbols to prune the search space.In other words (and hopefully more understandable words) if you have a problem that you can formulate as a boolean problem (A and B or C implies D), then you can change that formulation into conjunctive normal form ((A or B or C) and (A or D or E) ... ) and when thats done you can determine if it's actually possible to assign values to the variables that will make this true.The way the algorithm works is basically to pick a symbol (=variable) and assign it true or false, and then for each options recursively call itself until all variables are assigned. In order to minimize the search space it uses a couple of simple rules to shortcut through this search, like finding out which clauses only contained one unassigned symbol. It's also very important in what order it assigns variables.
Another challenge in this assignment was that the code provided for the assignment that we should use as a basis for our work and for testing was all in java (typical university assignment). We're all C# people but too lazy to rewrite everything in C#, so luckily we got the java-code working in J# and were able to base our code on it anyway (and I wouldn't be surprised if our execution performance is higher that if we had used java).

Read our project here.

Wednesday, March 21, 2007

Another day another tool: XPath


Here's a small 5-minute tool I made the other day, simply because I needed it and it was faster to make it than finding a good one online that does what I want it to (although I'm sure hundreds of them exist).

It's a simple XPath tester, that can test an XPath against a piece of XML, or a URL pointing to XML.
It even attempts to do some simple syntax highlighting of the XML.

The tool is online at www.mizar.dk/XPath. (In case you are wondering, Mizar.dk is an old domain I currently use all my silly projects and for various other demo/lab purposes. It's actually a leftover from a webdesign company I had together with Jesper years ago).

The XPath tester can also be used for simply showing XML, and it accepts a couple of query-line parameters so you can send link to XML with applied XPaths like this.
The query-line parameters are: XML (raw xml for source) XMLURL (if you instead want to retrieve xml from a specific location) and finally XPATH (guess what thats for).

Enjoy!

Monday, March 19, 2007

J# to the Rescue

I've always wondered what the purpose of J# was. I mean, Java and C# are quite similar, so I'd suppose that if people (for some weird reason) would prefer to code Java (instead of drinking it), they'd do so purely because they wanted to work within the java environment and the java VM. If they eventually decided to overcome their Microsoft fears and turn to the .NET framework, C# would seem like a natural choice. So it has indeed been puzzling my mind why J# is included with Visual Studio.
But today I found a use for it, and it really saved my day!
The next programming assignment (after the Connect 4 game) in the AI class I'm attending was handed out today and even though the assignment looks fairly interesting (implementation of DPLL algorithm to check satisfiable of Boolean expressions in CNF form) the source code that we are supposed to base our solution on was pure old-fashioned java. Of course, we are free to choose which language to solve the task with, but if we choose anything else than Java we'll have quite a programming task ahead of us, just making the code-base that we're supposed to extend on. After first considering learning java I was happy to remember that there might be another way out :-)
With shaking hands and a heart beating way too fast I started VS2005 and created a J# Class Library project. Then I copied the original source java code into newly created .jsl files with identical file names (copy+paste). As you probably can imagine the excitement was overwhelming when I tried to build for the first, but surprisingly there was only very few compiler errors...
A java HashMapSet that I discovered should be changed to an ArrayList in .NET, a couple of framework methods where Mocca (sorry, Java) used wrong casing, and finally a method that appeared to have been renamed in .NET, and I was good to go.
All that was left to do then, was to create a C# project in the same solution, reference the J#-library and I was good to go - well almost. In order to inherit one of the java-classes and override the methods we're supposed to implement we also needed to reference "vjslib" in our c# project in order to recognize the parameters to the methods we were overriding.

All in all I was very happily surprised to find that J# actually cured my pain.

Now we (me, Thomas and Peter) just need to actually solve the programming assignment we were given :-)

Connect 4: The code

Yesterday I handed in my code for the Connect4 game. Since the deadline has now passed I figured I might as well post it here, including the document I wrote to describe it. In case you want to play it again, do it here.

Download the code for this article here.

Introduction

In order to complete the assignment of making an implementation of a computer player for the game "Connect four", I started out by making an environment, a state model and a simple console based interface to test it.

The console based interface works by showing the current state as 7 columns with 6 rows in each. When it's the users turn, he/she should choose one of the 7 columns, by using the numbers 1-7. It is a very simple and basic UI because I wanted to my time on developing the search algorithm that will determine the computers moves.

To run the code, compile the project with a C# 2.0 compiler and run the executable. I've included solution and project files for VS2005 as well.

The console program also contains some experimental functionality that allows you to set up the computer to play against itself. This code was used when I was optimizing the weights of the evaluation algorithm, but is now commented out.

The Connect Four game has been "solved" by Victor Allis, and there is a "Perfect Play" path you can follow, that will force a win to the starting player. However I have chosen to disregard that playing strategy in this implementation since the goal was to learn about AI game-playing.

Class overview

Connect4: The main game class, that holds the current state and controls the flow of the game. In turn calls each of the two players, typically Computer and Human and executes their actions.

StateType: A state, containing the board, the number of moves made, and who is next in turn. It also contains methods to clone itself and perform a move, as well as evaluate if it is in a game-over state.

Computer: The class that holds all the logic related to the search algorithm. It is called like a player using the PlayerTurn(StateType state) method, and then begins to perform a modified MiniMax search with Alpha-Beta pruning to determine which action to choose. Its functionality is described below.

Human: The Human player. The PlayerTurn(StateType state) method is called by Connect4 every time it's the users turn. This then presents the state to the console and waits for input. When an action is retrieved it returns the action, a move is made and the turn changes.

Search Algorithm

The Search algorithm is a modified MiniMax algorithm with Alpha-Beta pruning. It is implemented in the "Computer" class, which controls the computer player.

Using double recursion is performs a depth-first search in the state-space until the cut-off method evaluates to true. At this point it uses the heuristic evaluation method to determine how close it (the computer player) is to winning. This value is passed up through the state tree, where each node either selects the maximum or the minimum of its values, depending on if it represents the computers turn or the players turn. The procedure is in fact quite similar to how a typical human player subconsciously would play the game: First enumerate the possible actions from the current state, then estimate the opponents move from any of the resulting states, and decide which would leave you in the most favorable state. However human (non-expert) players will typically only look 2-3 moves ahead, while the computer can search a much larger state-space.

In order to further improve the performance Alpha Beta pruning has been applied. This essentially is to remember the best (or worst) options for each node, so no time will be wasted exploring branches of the trees that's already identified as path that will not be played.

Heuristic Evaluation method

The Evaluation method is supposed to evaluate how good a given state is for the computer, e.g. how close is it to winning the game. After having experimented with several different models I found an approach that suited me the best in evaluation a state. In order to win the player must have 4 fields in a row, horizontally, vertically or diagonally. To find out how close both players was to this, I decided to examine all possible combinations of 4 fields that appear in a row in the 7*6 field board. First I would break it down to lines, horizontally, vertically and diagonally and evaluate each line. All lines I examine will of course need to be 4 fields or longer.

An example, a line with 7 fields would lead to the following possible winning combinations:

1 2 3 4 5 6 7 => 1234, 2345, 3456, 4567

For each of these combinations my evaluation method would examine the amount of them taken by each player. If none of the players or both of the players has selected fields in the same 4-field combination, the combination will be instantly discarded. Otherwise it will add to a state-score depending on the number of fields taken (how close it is to be a 4-in-a-row). Depending on the number, and if it's computer or player fields, a certain weight will be applied.

Determining the evaluation weights

Initially I started out with weights that was the square of the number of occupied fields in each block of four (e.g. 3-in-a-row would give a score of 9, 2 would be 4, 1=1) and if state resulted in 4 in a row it would get a big bonus / punishment of 10000/-10000.

However, it quickly became evident that I would at least need to raise the punishment if the player had 4 in a row, or take into account whose turn it was, to keep the computer player from focusing solely on its own block-building. After that optimization it became increasingly difficult to manually fine-tune the optimization of weights, since no matter how I adjusted them, I still couldn't beat the computer.

So I set up a game to allow two computer players to play against each other. The first player had the starting advantage, and the current weights. The second player would have almost the same weights, except one little difference at a time. If the second player would win in spite of the first player having the starter’s advantage, I would use the new weights as the current weights.

After having gone through this manual iteration 15-20 times, I decided that it was fairly optimized. If time had permitted I could imagine automating this process, perhaps using Simulated Annealing. However I felt that this was outside the scope of the current assignment.

Cut-off Method

Since the state space will typically be too large to explore in reasonable time, it's necessary to cut-off the search at a certain point.

First of all the cut-off method should be able to detect if we've actually reached a winning state, in which case it should always cut-off, alternatively it should determine if it is feasible to stop the search at the current level.

After experimentation I decided on a simple cut-off mechanism that cuts of the search after looking 6 moves ahead. 7 moves also seem possible within the allowed time-frame but I'm too impatient to wait more than 1-2 seconds for every move when playing.

An ideal approach would of course be a more intelligent cut-off mechanism that would keep searching until all available time was used, however time did not allow for such a solution at this time.

Tuesday, March 6, 2007

AI: Connect 4 Game

I have been kinda slow in posting these last couple of weeks. One of the reasons is of course that I've been busy coding (as always). This time however, it's actually real homework thats been keeping me busy.

In the AI course I'm taking at ITU we were assigned the task of making our own version of "Connect 4" - the well known board game where you drop coins into a board from the top and try to get four in a row, horizontally, vertically or diagonally - and preferably before your opponent.

I know that this game has already been "solved" and there exist a perfect solution for it. But nevertheless it's still interesting to make an algorithm that calculates the computers move.

In order to do this I've used a variant of the MiniMax algorithm, optimized with Alpha/Beta pruning. The basic idea is to search through a tree of possible actions and thereby thinking ahead to find the best possible action in order to go from an initial state to a goal state (win).
A state would typically consist of current game-board, and who's turn it is. From a state there's often 7 possible actions - each of the columns that it's possible to choose. If a column is full the number of possible actions decrease.
So, in theory, every time it's the computers time to move we would like to build a tree, starting at the initial (current) state and then branching out with the possible actions. Each of the possible actions will lead to a new node, that indicates the opponents move. The opponent again have a number of possible actions, and we assume that he will always pick the action that is best for him (= worst for us, the computer). This means that we can build a double-recursive method to traverse this tree, taking turns choosing the action with the maximum and minimum outcomes for us and thereby in the end evaluate which of the current actions available to us is the best (= where it's most likely we will win, even if the opponent does his best to screw it up).

If you haven't played around with algorithms like this before it might sound kinda complicated but it's really just applying the same method as most of us do in our heads when playing a game like this: "So, let me see.....if I select this column, then he will most likely select that column which means that I'm certain to win in the following move". However, where as humans often have difficulty thinking more than a couple of moves ahead, computers often have a better chance.

But there's one catch: Even with a game as simple as Connect 4, thinking several moves ahead scales terrible. Except for when the columns starts to fill up, there's 7 possible actions. For each of these actions the opponent can again choose 7 actions bringing us up to 7^2 (=49) states we need to consider. I've found that a typical game of Connect 4 often goes to at least 30 moves before a winner is found, meaning that we would have to examine something like 7^30 (=22539340290692258087863249) states. Of course Alpha-Beta pruning can help a lot on that, but in the end it'll still be too much to calculate in reasonable time.
Thats why it's a bit of a modified Minimax with AB pruning algorithm I use.
Instead of searching all states till it reaches a terminal state it will search until it reaches a certain cut-off depth. At that time it will apply some heuristic evaluation to the cut-off states to determine how close it is to winning in those states.
By experimentation I've so far found that to think 6-7 moves ahead makes the most sense performance wise. Perhaps even 8 will be a possibility if I optimize some more and allow the computer more time to think for each move.
The trick to achieving success even after "only" thinking 6 moves ahead is to have a good evaluation function that gives an accurate impression of how well the computer is doing compared to the player. I seem to have found one that works pretty well (so far only one of my friends have reported to have beat the computer once). The details of the evaluation function, along with code for the rest of my project you'll have to wait for until the project is officially handed in (there's going to be a computer vs computer contest in my class - not unlike the poker robot tournament I'm working on).

As for now, you can try out my game here! You always get to start first, you are red computer is blue. (And yes, I realize there are a couple of bugs still - no need to report them I'm working to fix them).

Wednesday, February 28, 2007

Loaderlock hell!

I recently installed VSTS and I'm still trying to learn my way around it.
One thing I noticed when using it was that I kept getting a "Loaderlock" MDA (Managed Debugging Assistant) whenever I tried to run a piece of interop'in code in debug mode. This was getting quite annoying, and it didn't seem like there was any problem in the code - rather in Visual Studio.
Naturally I started googling the problem and I found several places saying that all I had to do was to go to the "Debug | Exceptions" and turn off Loaderlock for the Managed Debugging Assistants (true, there was also a ton of other advices, like modifying the registry or installing vs service packs, but none of them seemed to do the trick). Unfortunatly my visual studio does not have "Exceptions" in the debug menu - or so it would seem....
Luckily my friend Moshe came to the rescue and found out that even though there's no menu-item called "Exceptions" in the debug menu, the Customize dialog will proof that there's supposed to be. And when we investigate further we found the keyboard shortcut to open it: CTRL+ALT+E.
After using this shortcut I was finally able to turn off the MDA that was annoying me in the first place. I wonder what actually caused it...

Monday, February 19, 2007

C# and keeping your lists sorted

Every once in a while I feel the need for a having a list in C# thats instantly sorted. And every time I always spend time examining the various lists available in C# and end up being quite annoyed.
Perhaps there is such a list in the .NET framework, but I havn't been able to find it yet.
Sure, there is the SortedList, but thats a Key/Value-style hashtable where it sorts on the keys. Sometimes thats fine, but most often I find that it's pretty useless. Especially since you can't have duplicate keys. You can also use the .Sort() method on an ordinary list of course. But what if you need to have a list that's always sorted, e.g. when you Add a new item to the list it's inserted in a sorted manner?! Calling .Sort() after each .Add() isn't really an option since it's way too slow.

So, as you might have expected, when I had the problem again this weekend I ended up writing my own implementation of a SortList.
It might not be pretty, but it seems to work and compared to the .Add();.Sort() alternative it's pretty darn fast :-)



public class SortList<T> : List<T>
where T : IComparable<T>
{

public new void Add(T Item)
{
if(Count==0){
//No list items
base.Add(Item);
return;
}
if(Item.CompareTo(this[Count-1])>0)
{
//Bigger than Max
base.Add(Item);
return;
}
int min = 0;
int max = Count-1;
while ((max-min)>1)
{
//Find half point
int half = min + ((max - min) / 2);
//Compare if it's bigger or smaller than the current item.
int comp = Item.CompareTo(this[half]);
if (comp == 0)
{
//Item is equal to half point
Insert(half, Item);
return;
}
else if (comp < 0) max = half; //Item is smaller
else min = half; //Item is bigger
}
if(Item.CompareTo(this[min])<=0) Insert(min,Item);
else Insert(min + 1, Item);
}
}


The code is based on a standard generic list, and it simply replaces the Add() method with a method that inserts item sorted. It does this by narrowing in on the list, always diving the list in two - much like one would solve the classical game of "Guess the number I'm thinking of".
To test it, I wrote a small test application, that generated an array with 10.000 random integers (values between 0 and 10.000). Then it timed how long it would take to add them to the SortList, and afterwards how long time it would take to Add+Sort them in a usual list (with Sort being called for each Add, cause we need a constantly sorted list).
On my workstation, SortList took about 40 ms to complete the task while List.Add+List.Sort took 2654 ms.
And in case you're wondering, the answer is Yes. The sorted contents was the same in both lists afterwards.

Wednesday, February 7, 2007

Structs vs. Classes

Obviously structs are supposedly a lot quicker than classes. However some time ago I heard rumours that in .NET 1.0 some error in the .net framework would cause them to be slower in some cases. Today for some reason I felt like making a small comparison in .NET 2.0.

So, I made a small console program with the following code:


public struct TestStruct
{
public int A;
public bool B;
//public string S;
}

public class TestClass
{
public int A;
public bool B;
//public string S;
public TestClass()
{
}
}

class Program
{
static void Main(string[] args)
{
int MaxIterations = 1000000;

DateTime t1 = DateTime.Now;
TestStruct[] ts = new TestStruct[MaxIterations];
for(int i=0;i<MaxIterations;i++)
{
ts[i] = new TestStruct();
ts[i].A = 42;
ts[i].B = true;
// ts[i].S = "Hey Joe";
}
DateTime t2 = DateTime.Now;
TestClass[] tc = new TestClass[MaxIterations];
for(int i=0;i<MaxIterations;i++)
{
tc[i] = new TestClass();
tc[i].A = 42;
tc[i].B = true;
//tc[i].S = "Hey Joe";
}
DateTime t3 = DateTime.Now;
TimeSpan ts1 = (TimeSpan) (t2 - t1);
TimeSpan ts2 = (TimeSpan) (t3 - t2);
Console.WriteLine("Struct Time: {0} ms", ts1.TotalMilliseconds);
Console.WriteLine("Class Time: {0} ms", ts2.TotalMilliseconds);
Console.ReadLine();

}
}

I ran it both with the struct/class containing a string, and without.
Here's the results I got from running it (in debug mode):

Structs/Classes without string: 20 ms / 270 ms (= classes are 13,5 times slower than structs)
Structs/Classes with string: 60 ms / 430 ms (= classes are 7 times slower than structs)

The reason I'm trying with and without strings is of course that strings are located on the heap, not on the stack as the other value-types, hence they are quite a bit slower.

It's naturally no surprise that structs are faster than classes - they should be - but it's nice to get an idea of exactly how big a difference there is.

Tuesday, February 6, 2007

New Functionality: Related Articles

Here's one of those small things I've been working on over the weekend.
If you're using IE, you should now be able to see a new widget-like thing in the bottom of the right-hand widgets. It's "Related Codeproject Articles".

The concept I use to get these related articles is somewhat similar to how the "Related Pages" functionality works in the MondoSearch/Sitecore integration - just based on another platform.

I've written a small piece of javascript that extracts the keywords on this page, and then calls a serverside function to perform an MSN Search on the keywords on one my my all time favourite websites, codeproject.

It's still quite experimental so I don't expect it to work in all scenarios - but possibly a few lucky readers will get to enjoy this functionality now :-)

If anyone is interested in more details as to how it's done, drop me a comment - I might be persuaded to share the code...