Thursday, March 20, 2014

A Chess Project, Part 9

Intro

As promised last week in Part 8, this week is all about the unit tests. We've got a pretty sizable chunk of code going in this project and so far we have no way to test any of it. I don't plan on creating a GUI for this solution (at least not in this series of blog posts), so unit testing is the way to go. Please view my unit testing series of blog posts (UT1, UT2, UT3, UT4) for a quick refresher on unit testing if necessary.

The Code

I think it will be easier to understand what's going on if I give you a link to the code at the top of the article this time, so here you go. Once you have it pulled down, extracted and compiled come on back for the remainder of the article.

Unit Tests

As you can see in the screenshot below, I created a new unit test project in the solution named BlogChess.Backend.Test.
This is where all our unit tests for the project are going. As of the time I am writing this, there are 57 unit tests in the solution. That is a bit too many for us to walk through in the blog, so at your leisure please dig through the code and see what's going on. The unit tests at this point cover the vast majority of our functionality from the backend dll. We are testing the validation code, board size, and all the valid moves for white. The only things left to test are the black piece move validation methods, and we'll do that in the next blog post.

As a result of all this unit testing, I found 2 other things necessary. First, I had to fix some bugs. I know what you're thinking: "But Pete, you don't make mistakes!". Well I appreciate the kind words (you were thinking them, admit it!), but yes I actually do make mistakes. I bet I fixed a half-dozen of them thanks to the unit tests. I won't detail all of them (mostly because I don't remember what they were at this point), but suffice it to say they would have made this project rather useless if they had been left to fester. The second thing I found necessary while creating unit tests was to have a way to visualize a chess board so that I could set up specific positions in the unit tests, and validate piece movement. Cue dramatic new bold sub-heading...

New Format, New Formatter/Parser (use of html agility pack)

...So, I created a new chess format. Sure arrays are great for representing a chess board, but which is easier to work with when creating a unit test? This...

{ {-4, -2, -3, -5, -6, -3, -2, -4}, {-1, -1, -1, -1, -1, -1, -1, -1}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {1, 1, 1, 1, 1, 1, 1, 1}, {4, 2, 3, 5, 6, 3, 2, 4} }


or this?

So, I invented a new chess format (it sounds a lot more impressive than it really is) and created a class to import the new format. Believe it or not, the above screenshot is pure html/css. There are no images in that. Well the screenshot itself is an image obviously, but it's showing just html/css.

How did we accomplish this? If you're moderately familiar with html/css, it's easier than you might think. It turns out that unicode defines special characters for each of the chess pieces, so as long as the font you are using supports these values, then you can use plain-old-text to represent the pieces. After that it's just a simple matter of putting a board around the pieces you want, and voila! You can look at some of the many samples I put in the solution, using the screenshot below as a guide to finding the files. The essence of the export format though is much simpler than these files allude to. All you need is elements on the page that have a data-col attribute of "a" through "h", and those same elements should have a data-row attribute of "1" through "8". These of course represent the 64 squares of the chess board. For ease of formatting I've elected to use a table (you could use div's, span's, or whatever other html element you feel like using), a modified sample of which you can see below.

    <table>
        <tbody>
<tr>
            <td>8</td>
            <td data-col="a" data-row="8"></td>
            <td data-col="b" data-row="8"></td>
            <td data-col="c" data-row="8"></td>
            <td data-col="d" data-row="8"></td>
            <td data-col="e" data-row="8">♚</td>
            <td data-col="f" data-row="8"></td>
            <td data-col="g" data-row="8"></td>
            <td data-col="h" data-row="8"></td>
        <tr>
</tbody></table>

For some more thorough and much cleaner looking samples with css to format them nicely, take a look at any of the unit test files in the solution as you see highlighted below. These samples all import perfectly fine using our new formatter object, and are also very easy to use the mark-1 eyeball to see what's going on with the board itself by just viewing the file in any modern web browser. Again the above html table is just a sample; obviously you'd need the other 56 squares to make a full board :)




Now, on to the design and code of the formatter. In the future I can see myself wanting to import and export various other chess formats/notations (there are plenty of them), so let's use an interface to represent any type of formatter. We'll call the interface IChessBoardFormatter. Then, because the format we want right now is html based, we'll just call the class HtmlChessBoardFormatter and have the class implement the new interface.


IChessBoardFormatter defines 4 methods: Import, Export, ExternalPieceToNative, NativePieceToExternal. Import and Export should be pretty self-explanatory. ExternalPieceToNative converts an external representation of a piece (in whatever format) to a native piece (a short value from -6 (black king) to +6 (white king). NativePieceToExternal just goes the other direction.

HtmlChessBoardFormatter is the class we'll use to translate boards between our internal representation(arrays of short) and the new human-friendly format (html). For the moment I didn't implement the capability to export; just import. For now I only need this class in order to read in html files for unit testing. It's too much code to put in this blog post, so open up HtmlChessBoardFormatter and take a look at the Import and ExternalPieceToNative methods; they're where the magic is.

If you've never used the Html Agility Pack I highly recommend it for your HTML parsing needs in .net. I've used it in the Import method of the formatter class in order to find html elements with a data-row and data-col attribute, as they are the elements that contain our chess pieces. Here's a sample of it:

            var document = new HtmlDocument();
            document.LoadHtml(sourceBoard);
            foreach (var node in document.DocumentNode.SelectNodes("//*[@data-row]"))
            {
                var sourceRow = node.Attributes["data-row"].Value;
                var sourceCol = node.Attributes["data-col"].Value;


Spiffy huh? Resilient html parsing made easy in .net.

What's Next?

As you may have guessed, we'll have to hit up the unit tests for black pieces/movement and any other public methods of the valid move calculator next week. I can finally see the light at the end of the space-time continuum though, we're almost ready to start coding our "best move" logic! And, thanks to these unit tests, we'll actually be able to suggest valid moves.

Resources

Wikipedia Chess
Wikipedia en Passant
Wikipedia Chess Symbols in UnicodeHtml Agility Pack

No comments:

Post a Comment