Showing posts with label .NET. Show all posts
Showing posts with label .NET. Show all posts

Friday, August 3, 2012

Troubles with Unity and Mono

Using Unity for my Othello game was not without issues. The biggest issue I experienced related to Unity's use of Mono. Here is why:

A depth-first search generates a lot data. Oridinally, in .NET, that would be fine. You create the data, process it and forget about it - the .NET garbage collector (GC) will release the data from memory when required. That isn't true of Mono 2.6. The garbage collector in Mono 2.6 is kind of rubbish. (See what I did there?) Mono 2.8 has a new garbage collector, but Unity 3.5 uses Mono 2.6. And my Othello game uses Unity 3.5.

For my Othello implementation, every turn by the computer player was a new search. It leaked memory all over the place because the GC wasn't dumping the data. It could easily use a gig of RAM over a game, even with shallow searches (depths of 5 or less).

To resolve this issue, I - though Andrew came up with the idea - used a struct instead of a class for the objects used in the search (EvaluationNode and GameState). I re-used memory by storing search results in arrays where the indexes were reset every turn of the game. This negated the need for garbage collection. In the source code, the classes that manage this process are called the EvaluationNodeBuffer and EvaluationNodeCollection.

These changes turned out to be a really good use and re-use of memory. It is also an excellent example to demonstrate the differences between a struct and a class. It also allowed me to search to much greater depths for the computer player.

A problem with this technique is that it makes it very difficult to write code to re-use part of the search tree between turns. Finding which parts of the tree to prune and to then re-organise the arrays and indexes would be technically tricky and CPU intensive. Therefore, for now, the computer player continues to re-searche all game states between turns. Furthermore, any sort of parallel programming to speed-up the search would be hindered by this approach.

What about Unity 4? That's out soon. Will that support the newer version of Mono? Unfortunately not.
We will be shipping Mono 2.6 with Unity 4.0. This will allow the same subsets of .NET features as in Unity 3, depending on the profile you choose in your player settings. (Unity 4 FAQ)

Friday, July 20, 2012

Computer Othello, Part 3: Othello computer opponent

After working on the game archive stats, I was ready to create a computer player. Previously, the computer player had played randomly (even Andrew was able to beat it!) To create a computer opponent, I needed to do two things; create an evaluation method and plug-in a search algorithm to find the best estimated play to make.

Evaluation

One of the trickiest things to do with the computer opponent is to evaluate the current game state. Given any arrangement of pieces, you need to be able to determine which of the possible plays is the best. You need an heuristic to do this. An heuristic is an estimation of what you think is a good play to make. The factors that I used to evaluate the state of the game were:

* Number of pieces
* Number of playable squares (usually called "mobility")
* Number of empty squares next to the player's piece ("the frontier", or "potential mobility")
* Various corner and edge patterns (edges and corners are usually better moves)

Pieces and mobility

The most naive heuristic is to count the number of pieces each side has. Ultimately, this is the only measure, piece count determines the winner. However, until the end game, it has little to do with who is going to win.

A less naive approach is to count how many plays can be made. A state where 7 plays are possible is almost certainly better than one where you have to skip your turn because you can't make a play. I had already calculated where a player could place a piece in their turn as part of the user interface. Therefore, I got the mobility measure for nothing.

Finding the empty squares next to a player's pieces was also relatively simple to implement. It was little more than a variation on the mobility algorithm.

Patterns

Image that elucidates where the X and
C-Squares are on an Othello board.
Implementing the patterns was relatively easy to do but it was one of the last additions to the evaluation function that I made. I had previously dismissed this approach as too simplistic to provide much of an improvement to the evaluation. I couldn't have been more wrong. Once I had coded the patterns, the computer player beat me easily. I've since beaten it once or twice, but it went from a disgustingly below average player to a better than average player.

The patterns that I check for are:
  • Corners (a good play for the current player)
  • X-Squares (bad play)
  • C-Squares (bad)
  • Corners and X-Squares (good)
  • Corners and C-Squares (good)
  • Edges (good)
Furthermore, I check for lots of combinations of these patterns. I.e., not only does a game state get points for a play on a corner, but gets even more points for the multiple corners. I didn't check to see if this approach improved play, but I suspect it would.

Piece stability and parity

There were a couple of major components of a good Othello evaluation function that I didn't include. These were piece stability and parity. Piece stability, i.e., finding the pieces that cannot be flipped, is one of the trickier things to determine. There is a good description of how to do it here. I couldn't think of a really efficient way to implement stability, so I left it out.

Parity, i.e., determining who plays last, was relatively simple to implement in its basic form. By default, white will always play last, and therefore has an advantage. For black to play last, someone has to miss a turn. The basic approach to parity didn't really seem to impact the performance of the computer player, so I left it out of the evaluation. A sophisticated form of parity - one where isolated parts of the board are evaluated for parity (an isolated section is one that is surrounded by pieces and edges) - seemed too tricky to implement, so I never tried.

Depth-First Search

I took my negamax, alpha-beta pruning negamax and negascout search methods from my noughts and crosses source code and adapted it to work for Othello. That was fairly easy to do, although my original code was a bit rubbish.

Initially, I thought I'd use negascout for Othello as it is the best of the three. However, for it to work effectively (i.e., better than an alpha-beta pruning negamax), it needs to do shallow searches of the game tree, or find some other way to have a pretty good attempt at pre-ordering the plays from best to worst. Negascout generally does a mini-search within a normal negamax search. It was a more involved task than I suspected. Once I had implemented the patterns approach to the evaluation function, I realised that my computer player was pretty good. Therefore, I decided not to pursue a negascout algorithm for Othello.

Opening book

With all the work that I did to be able to display the history of games to the human player (percentage of games that made a play, percentage of those where black wins), I was serendipitously writing the code for the computer player. The computer opponent uses this information in a similar way as a human.

End game search

One of the things that I didn't do for the computer player was an put any work into an end-game search. This sort of search is much deeper and tries to get search until the end of the game. Once a computer has this information, it'll know if it has won and exactly which moves to make to ensure victory. Until the end-game search, all other plays are calculated guessing. All I did to the computer player approaching end game was increase the search depth.

Conclusion

I completely underestimated how difficult it would be to create a competent computer player. I now have a much greater respect for people who managed to create computer players that are vastly superior to mine on machines that are vastly inferior to today's technology. It's true that .NET is not really up to the challenge (unmanged code like C and assembler would be much more suitable), but you'd think a modern processor using .NET could get close to Pentium using C. From my experience, it didn't. But in truth, it was the algorithms that weren't good enough. I would have many more things to do to be able to compete with other computer Othello players. E.g., stability detection, parity, better potential mobility, negascout, Multi-ProCut, end-game solving, much deeper searching, training and machine learning etc. There is an immensity of improvements that I didn't even touch on. I'm not unhappy with what I achieved, more very impressed that people have done so much better.

Sunday, December 18, 2011

Editing IE settings in the registry via C#

We have a weird setup at work where our Internet Explorer LAN configuration settings are periodically reset by people who manage the network. This brings IE and TFS to a crawl for the programmers. It doesn't seem to happen to everyone all at once or at a designated time. It can take a few minutes to realise what is going on as there are a number of issues that show similar behaviour. For new programmers, this is especially frustrating because they're not aware of this strange policy.

To the Windows registry Batman! I haven't played with this thing in years. I never really understood it very well. "Hierarchical database? All these weird names? Huh? What's going on?!" Seems so simple now.

Editing the registry in the .NET environment is easy. See here.
Finding how to change LAN proxy configuration settings took a little longer to find. Explained here.
Proxy exceptions? See here.

With the app working, all that is left to do is set the script to run every 15mins on the programmers' computers, and we're done.

Code looks like:

var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections"true);
 
if (key == null)
  throw new NullReferenceException("Registry SubKey not found.");
 
const string defaultConnectionSettings = "DefaultConnectionSettings";
var defaultConnectionSettingsValue = (byte[])key.GetValue(defaultConnectionSettings);
 
defaultConnectionSettingsValue[8] = 1;
key.SetValue(defaultConnectionSettings, defaultConnectionSettingsValue, RegistryValueKind.Binary);

Friday, December 16, 2011

Code generation in C#

I wrote my first code generator on the weekend. The real work - generating the C# class files - was trivial. Getting the generator into a state so that it could be easily used was more difficult.

There are a few ways you could do code generation in .NET. I explored:
I went with T4 Templates. They were a little obscure at first but provided the easiest entry point into using the code generator. You just build-up your source file and then right-click and select "Run Custom Tool" on the .tt file to generate the code.

I encounted a few issues when trying to write my template. They were:
  • Extension methods can't be embedded into a T4 Template. There is a workaround. I ended up converting my extension methods into normal methods (I only had two).
  • Accessing your assemblies is difficult in Visual Studio 2010. It has been explained very well. I managed to access my assemblies by writing something like <#@ assembly name="$(SolutionDir)..\DomainEntities\bin\Debug\Cpa.DomainEntities.dll" #>
The example T4 Templates I found on the Internet were a little more complex than what I wanted. (Examples should be as simple as possible.) I wanted to load a source file into memory and generate class files from that. Download my example template if you want something similar. It loads in a CSV file that has two columns (first column is the name of the class, second column the name of the property) and generates some puesdo-code of what the class files will eventually look like. I used this as my base template, then expanded it to create proper C# class files.