Tuesday 27 December 2011

Star Trek Voyager

I've finished watching the seven seasons of Star Trek: Voyager and, even if I enjoyed watching it, I also think it was the series with the most potential lost from all of them.

First of all, the show should have been called Star Trek Condescension. Each Star Trek series before it had some obnoxious characters, like Bones in The Original Series, even if he was saved by the clever interactions with his counterpart, Spock, or like Deanna Troi in Next Generation, intrusive and opinionated about just about everything that did not concern her, or like Kira Neris, who always had some cause to fight in the detriment of all her other colleagues on Deep Space 9. It was OK, it part of the concept. Voyager has broken that rule, making just about everybody as annoying as possible.

Top of the list: Captain Janeway, who was not only acting like the headmistress of a high school, placed there by divine powers to have children under her care and control, but who was also a complete hypocrite, changing her views whenever it suited her, but quoting larger than life "directives" whenever she wanted out of something. For all her talk of saving lives, if the show was reality, she would have killed her crew numerous times and would have insured just about every major force in the quadrant was an enemy of the Federation. And the worse part is that her acting was perfect: from the condescending tone of her voice to the raised eyebrows, from the hand on her hip to the dismissive smile, her body language was more obnoxious than anything she could have said.
The Voyager ship, with Borg alterations
Second in command Chakotay, a man of native-American origins, would have no problem breaking any rule when his Maki training would surface, only to justify anything by either invoking his spiritual ancestors or spouting truisms while fully inhaling before each sentence. While Janeway's condescension was authoritative, Chakotay's was always thuggish, but just as strong and annoying as his captain's. Also, he was making mistakes almost every time it didn't involve physical activity. Not the best choice for a second in comand.

Neelix deserves a special place in the annals of obnoxiousness, as a rodent like alien who comes on board as the lover of this pixie like beautiful blonde. After suffering more than a season rude and abusive bouts of jealousy from him, we spend the rest watching him intrude in everybody's personal lives from his self appointed position of "moral officer" and later of "ambassador". Only in the last season a Q is fusing his jaws and lips and removes his vocal cords, a humanitarian move who only lasted till a few minutes later. Blessed be the silence, though.

There are more, from the loud mouthed doctor who is "evolving" from very rude to intrusive and almost destroys the ship twice with all the good intentions to the duo B'elanna Torres and Tom Paris, who act so superior towards anybody not like them that they would have undoubtedly made the alpha couple in a high school drama.

A good thing about the series is the design. All the technology is consistent in aspect and apparent functionality from the start to the end of the series. Considering Voyager was produced during amazing technological advancements in television and computers, it was probably an effort not many noticed. The human component, so easily removable via computers and nanotechnology, was preserved during the entire length of the series, maintaining that theatrical feel and enforcing the idea that the sci-fi in the series was just a prop for some larger ideas. Unfortunately, the ideas was not that large, and were mostly human centric and ridiculously optimistic.

About the plot, the show is about a Federation starship stranded in the Delta Quadrant, seventy thousand light years from Earth. The way people travel is the most inconsistent part of the series, as they are always struggling to get home, while the same aliens are attacking recurrently, even with less advanced technology. How could they "ambush" Voyager, if they were left behind?

The holodeck and the holographic doctor were used extensively as a plot crutch, whenever they were out of ideas. Meetings with the Borg are common, even if the results are mind baffling. One of the most disgusting things in the series is the treatment of Seven of Nine, a Borg that is being coerced back to individuality despite numerous declarations that it wants to return to the Collective. But Janeway knows best and all the list of annoying characters above proceed thereafter to piss on the Borg heritage of Seven and insist on developing her "humanity". If Voyager would be watched by the people described in the show, it would undoubtedly be considered crass human propaganda for the Federation.

And still, for all the reliance on Borgs to move the plot on, the technological side of the equation was repeatedly ignored. Seven is part Borg and will remain so for the rest of the series, including nanoprobes in her blood. Yet she does not attempt to assimilate anyone, including enemies, when it would have been the best way of solving some of the problems. Borg technology is added to Voyager, but most often reluctantly and only temporary. Seven never develops cybernetic tools for herself, even after her implants save the ship several times. Also the Borg Collective is presented as a mindless community of interconnected people, but at the end a Queen of the Borg is revealed, who has total control and presents a target and a persistent enemy.

Bottom line, for a technological person as myself, I was almost attracted more to the Borg model than the Federation one. While the words "democracy", "freedom" and "openness" were spouted at every occasion, true freedom of thought was only tolerated on Voyager when the captain agreed. The Borg at least used the individual as a conduit for the general thought. The morality lessons in the series were simplistic and antiquated. Voyager, with the idea of a ship stranded somewhere, with problems that needed solutions with limited resources and lots of ingenuity, could have been a series to open minds. Instead, it force fed US concepts from the 60's.

Tuesday 20 December 2011

The Great Hunt (Wheel of Time book 2) by Robert Jordan

Book cover
I have to admit, the quality has increased dramatically in both writing and storyline in this second part of the Wheel of Time series. Robert Jordan's The Great Hunt follows our heroes in their quest to heal Mat, deliver the Horn of Valere and escape the endless machinations of the Aes Sedai.

I was saying in the review of the first book, The Eye of the World, that the story was ridiculously black and white, reminiscent of Lord of the Rings. The Great Hunt features good and evil Aes Sedai, sympathetic Darkfriends, political Cairhienians that see every action (or lack thereof) as a move in The Great Game of lords and not less than three new major threats, not counting the Black Aes Sedai, as well as parallel worlds, resurrected heroes and epic battles. So there is a good range of shades of grey, washing away the simplicity with which the series started. The characters gain volume, developing in their own unique ways.

There are some issues, though. Rand is exhibiting wonderful skill and ability exactly when needed, being pretty much clueless most of the time. Women again appear rather one sided: proud, intelligent, manipulative and always in some way of authority over men. The "The wheel spins as the wheel wills" quote is used way too much and the blatant logical hole ignored: if it does, then no one needs to get involved in anything, especially the Aes Sedai. Nynaeve find a new strength and acts more like a Wisdom and less like a scared little girl.

So I am caught up in this. Too interested to start reading the tech book I was planning and too much work at the office to really feel the need to. Let's see how the next book in the series will be.

A picture is worth a thousand words: North Korea in the dark

Check out this article. I haven't even read it yet, but the image they present is saying it all. Even someone such as myself, a firm believer in national sovereignty and the right to follow whatever twisted philosophy one chooses as long as it doesn't affect others, can't remain indifferent to it. What you see above and below North Korea are China and South Korea. You can tell North Korea apart, because it is the dark patch.

Friday 16 December 2011

Default parameters in Attribute constructor

Update February 2016: Tested it on Visual Studio 2015, with the Roslyn compiler, and the problem seems to have vanished.

Here is the now obsolete post:

A class in .Net can have default parameters, with values that are specified in the constructor signature, like this:
public MyClass(int p1,int p2=0) {}

If the class is inheriting from the Attribute class, then one can also specify property values when using it to decorate something, like this:
public class MyTestAttribute:Attribute {
public int P3 { get;set; }
}

[MyTest(P3=2)]
public class MyClass {}

What do you think this code would do?
public class MyTestAttribute:Attribute {
public MyTestAttribute(int p1,int p2=0) {}
public int P3 { get;set; }
}

[MyTest(1,P3=2)]
public class MyClass {}


Well, I tell you what is going to happen. Visual Studio and ReSharper both will see no problem with the syntax, but the compiler will issue an error based on the exception "error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type", but without specifying any file or line.

My guess is that it is trying to interpret the P3=2 line as an expression to be calculated and passed as the second attribute of the constructor. What I was expecting is to set the default value to the second constructor parameter, then set the property P3. The vagueness of the error points out to a possible bug.

Thursday 8 December 2011

The Chrome undocumented special URLs

It all started with this site that got stuck in the Google Chrome's DNS cache so that any changes to the Windows/System32/drivers/etc/hosts file were ignored. I didn't want to close all Chrome windows (since the DNS cache is application wide in Chrome), so I googled for an answer. And here it was, a simple url that, typed in the Chrome address bar, would allow me to clear the cache: chrome://net-internals#dns.

But there are a lot more cool things there: testing of failed sites, a log of browser network events, control over open connections and so much more. That got me curious on other cool chrome:// URLs and I found some links listing a lot of them.

I don't have the time to parse all these cool hidden Chrome URLs and review them in this blog entry, so I will just list some links and let you explore the goodness:
Google Chrome’s Full List of Special about: Pages
12 Most Useful Google Chrome Browser chrome:// Commands
About and Chrome URLs

Update: The Chrome url containing all others can be found at chrome://about/.

Tuesday 6 December 2011

The Eye of the World by Robert Jordan

Book coverThe Eye of the World is the first book in the series called The Wheel of Time, by Robert Jordan. Written in 1983, it is very similar to Lord of the Rings. It features a complex world of nations and races with their own languages and cool sounding names, a battle of good light versus evil dark that is so ridiculously polarized that at first I thought I couldn't finish the book, a party of different people in which the main character is a poor country sheppard boy, a quest that has the goal of saving the world via a long trek of personal transformation.

Actually, if I think about it, it seems almost entirely inspired by Lord of the Rings, with none of the innovations that appeared since playing any effect except maybe The Chronicles of Thomas Covenant the Unbeliever, when describing the Blight. Something that is clearly different in the book is the role of women.

While Tolkien had them placed high on a pedestal, queens and princesses that were supposed to inspire men but not be touched, Jordan presents them as important members of the party, with Moraine, the "wizard", holding most of the authority. Actually, it goes further than that, giving all women characters an almost indomitable ability to influence men. Only one female in the entire book is evil, and that for a very brief period of time, and none of them are weak in any way. It doesn't even matter much that The One Power has two sides, one female and one male, and that the male is tainted by The Dark One. Even if they would have magical power, men are doomed to be ruled by women in The Eye of the World and probably the rest of the books.

The evil Shai'tan is a dark entity with burning eyes and eternal rage, imprisoned yet powerful, corrupting everything he touches, while the power that opposes him is white pure light that heals, purifies and avenges. The only shred of ambiguity comes in the shape of The Children of the Light, a warrior sect dedicated to fight evil, but that are nothing more than pompous gang members that define evil as anything they don't like.

All and all the book was pleasant enough and, being December, I plan on reading at least the next book in the series before I start with a technical one. However, it doesn't come close to most of the books I've read recently. The simple design and clear inspiration is probably the reason why they want to adapt the story to television, now that the likes of Game of Thrones have shown the model successful. If the quality of the books does not improve in time, it may be so that it would be more effective to wait for the films instead of reading the book. Then again, I will make up my mind after I've read more. I have hope.

Wednesday 30 November 2011

Tuesday 22 November 2011

New Coma song for 2011

Coma has finally released a new song. It's completely free to listen and share. Here is the SoundCloud link for it: Un semn. You can also listen to it here.
Coma - Un semn [2011] by COMA-band-official

I wish it weren't such a light piece or that Dan Costea would have added some of his trademark screams in the background at least. I can't but think of Linkin Park, starting with great, powerful songs and continuing with whining in their next album. But being the first piece out, it may be a teaser for what it is to come.

Update:
There is a video for the song, and here it is:

Saturday 19 November 2011

Windows authentication dialog when trying to access the Reports path in a web site

I was trying to access http://localhost/Reports/Page.aspx, in other words an ASP.Net page in the Reports path of the local site. Instead, I was getting a Windows authentication prompt that had no business being there. At first I thought to debug the page, but it wouldn't even get there before I got the authentication prompt. I googled for it, but I didn't get far because I was looking for weird Windows authentication prompts, not for the specific location of my page: the Reports folder. It was stranger yet, as I stopped IIS and the authentication dialog was still appearing!

In the end, a colleague told me the solution: SQL Reporting Services is answering on the local Reports path! I stopped the service and voila! no more authentication prompt. Instead, a Service unavailable 503 error. This article explained things quite clearly. Even if you stop the service, you have to delete the access control list entry for /Reports with the command netsh http delete urlacl url=http://+:80/Reports or, I guess, restart the system after you set the Reporting Services service to Manual or Disabled.

Update: It is even easier to go to Sql Server Configuration Tools (in the Start Menu), run the Reporting Service Configuration Manager, then change the URL for the Report Manager URL to something other than Reports.

But what is this strange Access Control List? You can get a clue by reading about Http.sys API in Windows Vista and above and about Namespace Reservation. Apparently, one can do similar things on Windows Server 2003 and maybe even XP with the Httpcfg utility.

Thursday 17 November 2011

Lord of the Flies, by William Golding

Savage boys from the 1963 film
This book touches a very uncomfortable subject for me: the mindless, visceral hive mind of the crowd. There is nothing more horrible, I find, that being powerless in front of a mob of people united by only their stupidity and fear. Lord of the Flies is the archetypal book about this subject. It tells the story of a bunch of British kids stranded on a small island without any adult supervision. They create a parody of human society which ultimately fails horribly towards the end.

The book is short, but to the point. Sections of it are almost unbearable to read, not because it features monsters or supernatural creatures, but because you feel deep inside that it is the truth, that these things happen and that they do because of something deep inside each of us.

The only failing of the book, I feel, is that Jack and Roger are portrayed as classical psychopaths and it is clear from the beginning that one cannot empathise with them. A slightly longer story that would have made the effort to make these characters slightly likeable would have had an even deeper impact. In that case, I fear, the book would have become completely unbearable. People need their illusions about the society around them; shattering them completely would not do.

This book is a must read for any student of human psychology and one of the best books to reference at parties to make you look smarter than you are :-) I've actually read the book because I was doing that too much, but had only seen the movie. I wonder if I should get other works from William Golding, since I liked this one so much.

Wednesday 16 November 2011

SET versus SELECT in T-SQL

Let's start with an example:
DECLARE @SiteId INT
SELECT @SiteId=isnull(SiteId,0) FROM Orders WHERE OrderID=15
UPDATE Order_Sites SET SiteID=@SiteId


Can you spot the problem? What if SiteID in Order_Sites is not nullable? What if there is no order with OrderId 15?

That's right, when you select into a variable, you must be certain that the query returns any rows, otherwise the variable will not be set at all.

The solution is to add another operation that sets the value correctly. Here are three possible options:
  • Set @SiteId to 0 before the select.
  • Set @SiteId to isnull(@SiteId,0) after the select and simplify the select to not contain the isnull.
  • Use the select as an argument of the isnull operation:
    SET @SiteId= isnull((select SiteId from Master_orders where OrderID=-1),0)
    Yes, you can do that.


Either way, always pay attention to this gotcha in using SQL.

Thursday 10 November 2011

Have children, please!

"Oh, no! Siderite's blog has been hacked", you will think immediately. People who know me know how I feel about having children, and that is: I don't feel anything. I seem to lack that inner feeling that makes people procreate for no good reason. And while I am at the subject, I do not deny the existence of this feeling in the world and I don't believe most people are like described below, at least I hope so, but it just hit me that so many times, people have said something and meant another. I will elaborate. Stay assured, my blog was not hacked yet.

You know those dreams we have when we are young? We will get rich and famous, we will find true love, we will be the best at what we do, we can do anything if we want, we can quit anytime (but we don't want to), we will always have time to lose weight and go to the gym, etc. We actually believe those things will happen for most of our youth and early adulthood and some of us actually do something about it, while the most just expect it will happen if they wait long enough.

Well, after a while, reality hits home and we understand that we actually cannot do all of those things, maybe none of them and that our life will not get any better than it is on its own. Some people, at this moment in life, start thinking about children. This way they can delay losing hope by passing it on to their children. That's why many parents are disappointed with their offspring, not because they actually thought their children were special, but because they forced themselves to believe it. In the end, they spawn other normal people, just like them.

You may feel that I am too much of an asshole saying these things, even more than usual, but I don't think so. You see, the assholes are people who spontaneously start advising you to have children, like they already have or plan to. When they say that, they are saying "For a moment now, I thought you might be better than me and I felt a little threatened. Have some children, please, so I can feel better about myself". Have you ever heard the one about children "fulfilling you"? Do I look half full to you? My life does have meaning and I am quite happy with it. I don't need children, therefore I am not having any. If I look unhappy, it may be because I still have hopes for myself and I still believe I can do better. I get disappointed in myself because I expect a little more from me.

I had to write this post because the only times I actually considered having children for more than one second was when I was depressed for not doing something as well as I wanted or when not having time to fulfil ALL of my dreams. I just realised that. There was never a content, happy time in my life when the thought ever crossed my mind.

Now I know that alphish or hormonal males and females do naturally feel the need to have children. I understand the overall need for our species to procreate (although, not right now, when we are too many to fart without a human nose having to smell it). I also don't begrudge or disrespect people having children (as long as they keep them out of my face). However, think long and hard before you tell someone to have children. What is actually the reason you are saying that? Aren't you a bit of an asshole, even if it were any of your business?

Monday 7 November 2011

Careful when reusing Javascript RegExp objects

I had this operation on a Javascript object that was using a complex regular expression to test for something. Usually, when you want to do that, you use the regular expression inline or as a local variable. However, given the complexity of the expression I thought it would be more efficient to cache the object and reuse it anytime.

Now, there are two gotchas when using regular expressions in Javascript. One of them is that if you want to match on a string multiple times, you need to use the global flag. For example the code
var reg=new RegExp('a',''); //the same as: var reg=/a/;
alert('aaa'.replace(reg,'b'));
will alert 'baa', because after the first match and replace, the RegExp object returns from the replace operation. That is why I normally use the global flag on all my regular expressions like this:
var reg=new RegExp('a','g'); //the same as: var reg=/a/g;
alert('aaa'.replace(reg,'b'));
(alerts 'bbb')

The second gotcha is that if you use the global flag, the lastIndex property of the RegExp object remains unchanged for the next match. So a code like this:
var reg=new RegExp('a',''); //same as: /a/;
 
reg.test('aaa');
alert(reg.lastIndex);
 
reg.test('aaa');
alert(reg.lastIndex);
will alert 0 both times. Using the global flag will lead to alerting 1 and 2.

The problem is that the solution to the first gotcha leads to the second like in my case. I used the RegExp object as a field in my object, then I used it repeatedly to test for a pattern in more strings. It would work once, then fail, then work again. Once I removed the global flag, it all worked like a charm.

The moral of the story is to be careful of constructs like _reg.test(input);
when _reg is a global regular expression. It will attempt to match from the index of the last match in any previous string.


Also, in order to use a global RegExp multiple times without redeclaring it every time, one can just manually reset the lastIndex property : reg.lastIndex=0;

Update: Here is a case that was totally weird. Imagine a javascript function that returns an array of strings based on a regular expression match inside a for loop. In FireFox it would return half the number of items that it should have. If one would enter FireBug and place a breakpoint in the loop, the list would be OK! If the breakpoint were to be placed outside the loop, the bug would occur. Here is the code. Try to see what is wrong with it:
types.forEach(function (type) {
if (type && type.name) {
var m = /(\{tag_.*\})/ig.exec(type.name);
// type is tag
if (m && m.length) {
typesDict[type.name] = m[1];
}
}
});
Click here to see the answer

Saturday 5 November 2011

THE PLEASURES OF STATISTICS: The Autobiography of Frederick Mosteller.

Book cover
I was looking for autobiographies, since I liked quite a few of them lately and I felt like more, and so I got two. One is interesting because it is finally in print after 100 years since the author's death. I am talking about the first volume of Mark Twain's biography. However, I really could not make myself read it: the language was so pompous and the content so lame that I felt pain trying to.

Not so the second book, which seemed even more unlikely for me to like it: THE PLEASURES OF STATISTICS: The Autobiography of Frederick Mosteller, but which I did. It started with a few projects that Fred Mosteller participated in, explaining the day to day concerns and situations of a statistician while working on them. I thought at first that the book is going to be all like this, so after about a third I was about to abandon the read. You see, it was all very interesting from a professional statistician's point of view, but I wanted the more personal viewpoint of the man. And so I got it. Suddenly the book changed pace and went with the early life and education of Mosteller. The end of the book again covered some cases of work, but this time with a personal touch that explained the motivation behind the acts. And finally, the editor's epilogue, written from testimonies of friends and colleagues.

In this review, a Theodore M. Porter argues that the autobiography was flawed, as it covered little of his family life and couldn't reconcile the different viewpoints that appeared in the book, like the scientific and personal. But I disagree. The autobiography was unfinished and I guess the editor did the best he could with what he had, but it couldn't have been a lot different from what Mosteller himself intended. You start with the actual work: statistics, explained in layman's terms, then you continue with the actual man, explaining the origins and education, then you get back to statistics, but examining the work from the personal viewpoint of the man described. Yes, he could have written about his family more, but it wouldn't have been about statistics. The little he does write about his wife is about how supportive she was throughout his career. And yes, the tone of the book is a bit clinical, but this is how the writer actually thought like; he was a scientist in the true sense of the word and I liked this book exactly because it made me understand how such a man thinks and feels.

Even more than the structure of the book and the insight in the mind of a conscientious and brilliant scientist what I liked most is the peek at the world in the middle of the 20th century and how strikingly different it was from what we see today. The concerns of a teacher towards the best method to get his students to understand and like the subject, the way people were getting together to solve problems and worked for years on a book or bunch of science papers, the way academia was also supportive, not only political, and most of all, to see how people can be both brilliant and empathic, both clinical in science and warm in person.

I wouldn't recommend this book to everyone. I had a hard time reading it to the end and paying attention to every bit. Nor should one study it like a school manual, because as far as I see, the book is about a man's soul and you only have to understand and feel that. Whether it is because of my autobiography fad or because I resonated with the man or for some other reason, the bottom line is that I enjoyed reading the book. Maybe you will too.

Saturday 29 October 2011

Hosting chess games in your blog posts

Finally I have found the chess game viewer I wanted in order to publish my own PGN games in the blog! The name is Chess Tempo PGN Viewer and it is well written, fast, supports annotations and variations and is very configurable. Most of all, it is all Javascript (sorry for the occasional Java prompts. I almost caved in and did what I swore I wouldn't ever do: have Java applets in my blog).

Please tell me if you have issues with the new chess viewer.

The Stonewall attack chess opening

Luckily for me, some chess videos are for beginners like me. Here is one from OnlineChessLessons, describing a simple, clear opening called The Stonewall Attack. After watching the video I played a game with my trusted Nokia phone and managed to create a game starting with this opening. I then analysed it using chess engines Houdini and Rybka and annotated it manually. Check it out, including the variations.

Video first:

Make sure you also follow the article attached to the video.

And now my game:
[Event "29/10/2011 1:08:49 pm"]
[Date "29/10/2011"]
[White "Siderite"]
[Black "Nokia Easy5"]
[Result "1-0"]
[ECO "D05"]
[Opening "Colle"]
[Variation "5.c3 Nc6 6.Nbd2 Bd6 7.O-O O-O"]
[TimeControl "600"]
[Termination "normal"]
[PlyCount "59"]
[WhiteType "human"]
[BlackType "computer"]


1.d4 e6 2.e3 d5 3.Bd3 Bd6 4.f4 Nf6 5.Nd2 O-O 6.Ngf3 c5 7.c3 Nc6 8.O-O c4 9.Bc2 Ng4
{At this point, the engines suggest Bxh7, a classic sacrifice.}
10.Qe2
{However, I moved the queen to defend e3.}
( 10.Bxh7+ Kxh7 11.Ng5+ Kg8 12.Qxg4
{This variation wins the h7 pawn, but moves away from the spirit of the original game.}
12...Qf6 13.e4 ) 10...f5
{At this point, engines suggest Ne5, followed by knight exchange from black.}
11.h3
{I chose to shoo the knight away, but weakening g3, where the knight would love to come later.}
( 11.Ne5 Ngxe5 12.dxe5 Be7 13.b4 ) 11...Nf6 12.Ne5 Qa5 13.g4 fxg4 14.hxg4 Qb6
{engines would want me to attack the knight on f6 before moving the rook out.}
15.Rf2 ( 15.g5 Bxe5 16.dxe5 Ne8 17.Kg2 g6 18.b3
{Engines decide to try a queen side attack as well, in order to weaken the black pawn chain. I was not interested in that.}
) 15...h6
{engines suggest attacking the rook and with Ng6, which is a natural attacking move and a lovely outpost.}
16.Rh2 ( 16.Ng6 Rf7 17.g5 Nh7 18.Qh5 Ne7 19.Nxe7+ Rxe7 20.gxh6 Nf8 21.Rg2 Qc7 22.Qg5
{However at this point the game moves into queen side attacks and a slower attacking pace.}
) 16...Ne7 17.g5 hxg5 18.fxg5 Bxe5 19.dxe5 Nd7
{engines suggest now a beautiful move: Rh8, followed by a munching of black pieces or/and mate. Make sure you check out the variation.}
20.Nf3 ( 20.Rh8+ Kf7 ( 20...Kxh8
{Taking the rook leads to a quick mate.}
21.Qh5+ Kg8 22.Bh7+ Kh8 23.Bg6+ Kg8 24.Qh7# ) 21.Qh5+ g6 22.Bxg6+ Nxg6 23.Qh7+ Ke8 24.Qxg6+ Kd8 25.Rxf8+ Nxf8 26.Qf6+ Ke8 27.Nf3
{Try this variation on a chess engine to see it to the end. White is only one pawn up, but it is a passed one. The king is safe as well.}
) 20...Rb8 21.g6 Nf5 22.Rh3 Nh6 23.Kh1
{I felt like the pin on e3 was annoying and stopping me from using the black bishop. The engines recommend moving Qh2 instead, which is much better.}
( 23.Qh2 Nc5 24.Ng5 Ne4 25.Bxe4 dxe4 26.Nh7 Qc7 ( 26...Rd8
{If you wanted to know why black did not move the rook when attacked by the knight, follow this variation through.}
27.Rxh6 Rd1+ 28.Kg2 Qd8 29.Nf6+ Kf8 30.Rh8+ Ke7 31.Qh4 Rd2+ 32.Bxd2 b6 33.a4 Qxd2+ 34.Kh1 Qe1+ 35.Rxe1 Bb7 36.Rxb8 Bc8 37.Rxc8
gxf6 38.Qh7# ) 27.Nxf8 Qd8 28.Rxh6 Qg5+ 29.Kf2 Qxh6 30.Qxh6 gxh6 31.Nh7
{At this point white is a knight up, but what a boring continuation.}
) 23...Nf5 24.Qh2 Ng3+
{Engines suggest I move the king and concentrating on the attack, but I took the knight with the rook.}
25.Rxg3 ( 25.Kg1 Qxe3+ 26.Bxe3 Ne2+ 27.Kg2 Nf4+ 28.Bxf4 Rf5 29.Rh8# ) 25...Re8 26.Qh7+ Kf8 27.e4
{At this point, a mate in 8 is found.}
27...Qc7
{But with this move, mate will happen in 4.}
28.Bg5 Nf6 29.exf6 Rd8 30.Qh8# 1-0


Enjoy!

P.S. I am currently looking for a method of displaying the game as I want it on the blog: dynamic, with annotation and variation support, preferably something that is not Java and optimally something that reads the PGN from a span and replaces it with a nice looking chess interface. Right now, the usual game engine fails for some reason I need to analyse.

Friday 28 October 2011

Optimizing Visual Studio solutions and projects or how good intentions can pave a lot of road

I've had a horrible week. It all started with a good Scrum sprint (or so I thought) followed by a period of quiet in which I could concentrate on my own ideas. And one of my ideas was to optimize the structure of the solution we work on, containing 48 projects, in order to save space and compilation time. In my eyes, I was a hero, considering that for a company with tens to hundreds of devs, even a one second increase in speed would be important. So, I set up doing that.

Of course, the sprint was not as good as I had imagined. A single stored procedure led to not less than four bugs in production, with me being to blame for them all. People lost more time working on reproducing the bugs, deploying the fix, code reviewing, etc. At long last I thought I was done with it and I could show everyone how great the solution looked now (on my computer) and atone for my sins.

So from a solution that spanned from 700Mb clean and 4Gb after compilation, I managed to get it to a maximum of 1.4Gb. In fact, it was so small I could put it all in a Ram disk, leading to enormous speeds. In comparison, a normal drive goes to about 30MB per second, an SSD drive (without encryption) goes to about 250MB/s, while my RamDisk was running at a whooping 3.6GB/s. That sped up the compilation and parsing of files. Moreover, I had discovered that MsBuild has this /m parameter that makes it use more processors. A compilation would go to about 40 seconds, down from two minutes and a half. Great! Alas, it was not to be so easy.

First of all, the steps I was considering were simple:
  • Take all projects and make them have a single output folder. That would decrease the size of the solution since there would be no copies of the .dll files, Then the sheer speed of the compilation would have to increase, since there would be less copying and less compilation.
  • More importantly, I was considering making a symlink to a RAM drive and using it instead of the destination folder.
  • Another step I was considering was making all references to the dll files in the output folder, not to the projects, allowing for projects to be opened independently.


At first I was amazed the solution decreased in size so much and I just placed the entirety of it into a RAM drive. This fixed some of the issues with Visual Studio, because when I was selecting a file through a symlink to add as a reference, it would resolve to the target folder instead of the name of the symlink. And it was't easy either. Imagine removing all project references and replacing them with dll references for 48 projects. It took forever.

Finally I had the glorious compilation. Speed, power, size, no warnings either (since I also worked on that) and a few bug fixes thrown in there for good measure. I was a god! Then the problems appeared.

Problem 1: I had finished the previous sprint with a buggy stored procedure committed to production. Clients were losing money and complaining. That put a serious dent in my pride, especially since there were multiple problems coming from both less attention to how I wrote the code to downright lack of knowledge of the flow of the application. For the last part I am not really the only one to blame, but it was my responsibility.

Problem 2: The application was throwing some errors about the target framework of a dll. It was enough to make me understand a major flaw in my design: there were .Net 3.5 and .Net 4.0 assemblies in the solution and placing them all in the same output folder would break some build scripts. Even worse, the 8 web projects in the solution needed to have their output in the bin folder, so that IIS would find them. Fixed it only to see the size of the solution rise back to 3Gb.

Problem 3: Visual Studio would not be so smart as to understand that if a project is loaded, going to the declaration of a member in the compiled assembly means I want to see the actual source, not the IL code. Well, sometime it worked, but sometimes it didn't. As a result I restored the project references instead of the assembly references.

Problem 4: the MsBuild /m flag would do wonders on my machine, but it would not do much on the build server. Nor would it do its magic on slower, less multiprocessor computers than my own.

Problem 5: Facing a flood of problems coming from me, my colleagues lost faith and decided to not even try the modifications that removed the compilation warnings from the solution.

Conclusion: The build went marginally faster, but not enough to justify a whole week of work on it. The size decreased by 25%, making it feasible to put it all in a RAM Drive, so that was great, to the detriment of working memory. I still have to see if that is a good or a bad thing. The multiprocessor hacks didn't do much, the warnings are still there and even some of my bug fixes were problematic because someone else also worked on them and didn't tell anyone. All in a week's work.

Things I have learned from all this: Baby steps. When I feel enthusiasm, I must take it as a sign of trouble. I must be dispassionate as an ice cube and think things through. If I am working on a branch, integrate the trunk into it every day, so as to not make it harder to do at the end. When doing something, do it from start to finish, no matter what horrors I see while doing it. Move away from Sodom and not look back at it. Someone else will fix that, maybe, you just do your task well. When finishing something, commit it into the source control so it can easily be reverted through a single atomic operation.

It is difficult to me to adjust to something that involves this amount of planning and focus. I feel as if the chaotic development years of my youth were somewhat better, even if at the time I felt that it was stupid and focus and planning was needed. As a good Romanian, I am neurotic enough to see the worst side of everything, master at complaining about it, but incapable of actually doing something. Yeah... this was a bad week.

Saturday 22 October 2011

Two interesting news and no news

No news from my personal or work fields. However, I've found two interesting news just today and I wanted to share them.

First, the development of a camera to capture pictures that you can focus later. Although I have heard of solid metal lenses that would be less than 1$ to make and would achieve the same effect, the only actually functioning system I've heard of so far is the Lytro Living Picture camera. Here is an Ars Technica article on it and here is a YouTube video demo.

The second news is more IT related. It involves the cryptographic standards for XML, as defined by W3C. They failed! Here is an article about how they were cracked by using a vulnerability in the Cipher Block Chaining and here is a link to their press release.

Monday 17 October 2011

How Life Imitates Chess, by Garry Kasparov

Book cover
The complete title of the book is How Life Imitates Chess: Making the Right Moves, from the Board to the Boardroom, which is a mouthful, but very precise. It does explain how principles of chess, economics and politics apply in all three fields and how Garry Kasparov has evolved from chess player to world champion and, nowadays, into a political anti-Putin figure.

If you ask me, abandoning chess to get into business and worse, politics, it's a complete loss. However I do understand the guy, he got bored. Someday I may abandon computer programming.

Back to the book, though, it felt a lot like The Art of Learning, also written by a brilliant chess player (who incidentally also abandoned chess... hmm). It was more precise, most logical, though, looking at things from a more of a clinical perspective. I would have wanted to learn more about Kasparov's relationship with Karpov, for example, since he is always calling him his nemesis, but never says anything about how he felt about the guy.

This book is peppered with good advice, historic comparisons and great quotes from chess players and great men. Also, short descriptions of the relationship between famous "chess pairs" are giving the book an extra chess dimension. All in all I recommend it highly, although it felt more like a useful reference than a soul book like The Art of Learning.

Thursday 13 October 2011

Tuesday 11 October 2011

Optimal use of case insensitive Dictionary in .Net

A question arose at the office today: What is faster? Using a Dictionary<string,object> with a StringComparer.OrdinalCaseInsensitive constructor parameter or using a normal constructor call and using ToLower on the key before using it. The quick answer: using ToLower on the key.

The longer answer is that StringComparer.OrdinalCaseInsensitive implements IEqualityComparer<string> by using a native code function for GetHasCode(), which is very efficient. Unfortunately, it must use the case insensitive string comparison on both input key and stored keys, while calling ToLower on the keys before using them makes the comparison only once.

Wednesday 28 September 2011

Geek Love, by Katherine Dunn

book coverGeek Love is another title that mislead me. Geek, in this book, refers to the original definition of the word "A carnival performer who does wild or disgusting acts" and not a cool lovable geek as myself. The subject is "controversial": the life of an albino hunchbacked dwarf woman as a member of a carnival family.

There is a lot going on in the book. Carnival people poison themselves in order to make their children as freaky as possible. Said children are then raised only if they are strange enough. The failures are either preserved in glass jars if they are too mutated, or given away if they are too normal. The successes range from the main character, to a sociopath hairless cult leader with flippers instead of members, to siamese sisters that have the same lower body to a telekinetic God like child who only wants to be loved and gets manipulated into doing stuff for others. The sisters later give birth to a grotesquely obese child, fathered by a man with half his face blown up who squirts inside their vagina in his death moment. The death and the face blowing are unrelated. There is more, like a rich heiress who pays beautiful women to mutilate themselves in order to have a better life, unencumbered by the sexual desire of her subjects or of the people surrounding them.

The book itself was pretty innovative, but rather boring. If I had an alternative, I wouldn't have finished it, but as I had not, I am a bit proud of having finished it. If nothing else, the book is strange enough to be interesting. Also the writing is pretty good, introverted, taking the reader inside the mind of a person who considers normal people too bland and treasures her deformity and that of her daughter, fathered through telekinesis with the sperm of her brother.

Don't get me wrong, it was not painful reading the book and I do not regret having reading it. However I wish there was something more interesting in the story other than the strangeness of one's thoughts.

Sunday 25 September 2011

Portable Game Notation and parsing it with regular expressions

Short version: here is the link to the uploaded .NET regular expression. (look in the comment for the updated version)

I noticed that the javascript code that I am using to parse PGN chess games and display it is rather slow and I wanted to create my own PGN parser, one that would be optimal in speed. "It should be easy", I thought, as I was imagining getting the BNF syntax for PGN, copy pasting it into a parser generator and effortlessly getting the Javascript parser that would spit out all secrets of the game. It wasn't easy.

First of all, the BNF notation for Portable Game Notation was not complete. Sure, text was used to explain the left overs, but there was no real information about it in any of the "official" PGN pages or Wikipedia. Software and chess related FTPs and websites seemed to be terrible obsolete or missing altogether.

Then there was the parser generator. Wikipedia tells me that ANTLR is pretty good, as it can spew Javascript code on the other end. I downloaded it (a .jar Java file - ugh!), ran it, pasted BNF into it... got a generic error. 10 minutes later I was learning that ANTLR does not support BNF, but only its own notation. Searches for tools that would do the conversion automatically led me to smartass RTFM people who explained how easy it is to do it manually. Maybe they should have done for me, then.

After all this (and many fruitless searches on Google) I decided to use regular expressions. After all, it might make a lot of sense to have a parser in a language like C#, but the difference in speed between a Javascript implementation and a native regular expression should be pretty large, no matter how much they optimize the engine. Ok, let's define the rules of a PGN file then.

In a PGN file, a game always starts with some tags, explaining what the event is, who played, when, etc. The format of a tag is [name "value"]. There are PGN files that do not have this marker, but then there wouldn't be more than one game inside. The regular expression for a tag is: (\[\s*(?<tagName>\w+)\s*"(?<tagValue>[^"]*)"\s*\]\s*)+. Don't be scared, it only means some empty space maybe, then a word, some empty space again, then a quoted string that does not contain quotes, then some empty space again, all in square brackets and maybe followed by more empty space, all of this appearing at least once.

So far so good, now comes the list of moves. The simplest possible move looks like 1. e4, so a move number and a move. But there are more things that can be added to a move. For starters, the move for black could be following next (1. e4 e5) or a bit after, maybe if there are commentaries or variations for the move of the white player (1... e5). The move itself has a variety of possible forms:
  • e4 - pawn moved to e4
  • Nf3 - knight moved to f3
  • Qxe5 - queen captured on e5
  • R6xf6 - the rook on the 6 rank captured on f6
  • Raa8 - The rook on file a moved to a8
  • Ka1xc2 - the knight at a1 captured on c2
  • f8=Q - pawn moved to f8 and promoted to queen
  • dxe8=B - pawn on the d file captured on e8 and promoted to bishop


There is more information about the moves. If you give check, you must end it with a + sign, if you mate you end with #, if the move is weird, special, very good, very bad, you can end it with stuff like !!, !?, ?, !, etc which are the PGN version of WTF?!. And if that is not enough, there are some numbers called NAG which are supposed to represent a numeric, language independent, status code. Also, the letters that represent the pieces are not language independent, so a French PGN might look completely different from an English one. So let's attempt a regular expression for the move only. I will not implement NAG or other pieces for non-English languages: (?:[PNBRQK]?[a-h]?[1-8]?x?[a-h][1-8](?:\=[PNBRQK])?|O(-?O){1,2})[\+#]?(\s*[\!\?]+)?). I know, scary. But it means a letter in the list PNBRQK, one for each possible type of chess piece, which may appear or it may not, then a letter between a and h, which would represent a file, then a number between 1 and 8 which would represent a rank. Both letter and number might not appear, since they represent hints on where the piece that moved was coming from. Then there is a possible letter x, indicating a capture, then, finally, the destination coordinates for the move. There follows an equal sign and another piece, in case of promotion. An astute reader might observe that this also matches a rook that promotes to something else, for example. This is not completely strict. If this long expression is not matched, maybe something that looks like OO, O-O, OOO or O-O-O could be matched, representing the two possible types of castling, when a rook and a king move at the same time around each other, provided neither had not moved yet. And to top it off, we allow for some empty space and the characters ! and ? in order to let chess annotators express their feelings.

It's not over yet. PGN notation allows for commentaries, which are bits of text inside curly brackets {what an incredibly bad move!} and also variations. The variations show possible outcomes from the main branch. They are lists of moves that are enclosed in round brackets. The branches can be multiple and they can branch themselves! Now, this is a problem, as regular expressions are not recursive. But we only need to match variations and then reparse them in code when found. So, let's attempt a regular expression. It is getting quite big already, so let's add some tokens that can represent already discussed bits. I will use a @ sign to enclose the tokens. Here we go:
  • @tags@ - we will use this as a marker for one or more tags
  • @move@ - we will use this as a marker for the complicated move syntax explained above
  • (?<moveNumber>\d+)(?<moveMarker>\.|\.{3})\s*(?<moveValue>@move@)(?:\s*(?<moveValue2>@move@))?\s* - the move number, 1 or 3 dots, some empty space, then a move. It can be followed directly by another move, for black. Lets call this @line@
  • (?:\{(?<varComment>[^\}]*?)\}\s*)? - this is a comment match, something enclosed in curly brackets; we'll call it @comment@
  • (?:@line@@variations@@comment@)* - wow, so simple! Multiple lines, each maybe followed by variations and a comment. This would be a @list@ of moves.
  • (?<endMarker>1\-?0|0\-?1|1/2\-?1/2|\*)?\s* - this is the end marker of a game. It should be there, but in some cases it is not. It shows the final score or an unfinished match. We'll call it @ender@
  • (?<pgnGame>\s*@tags@@list@@ender@) - The final tokenised regular expression, containing an entire PGN game.


But it is not over yet. Remember @variations@ ? We did not define it and with good reason. A good approximation would be (?:\((?<variation>.*)\)\s*)*, which defines something enclosed in parenthesis. But it would not work well. Regular expressions are greedy by default, so it would just get the first round bracket and everything till the last found in the file! Using the non greedy marker ? would not work either, as the match will stop after the first closing bracket inside a variation. Comments might contain parenthesis characters as well.

The only solution is to better match a variation so that some sort of syntax checking is being performed. We know that a variation contains a list of moves, so we can use that, by defining @variations@ as (?:\((?<variation>@list@)\)\s*)*. @list@ already contains @variations@, though, so we can do this a number of times, to the maximum supported branch depth, then replace the final variation with the generic "everything goes" approximation from above. When we read the results of the match, we just take the variation matches and reparse them with the list subexpression, programatically, and check extra syntax features, like the number of moves being subsequent.

It is no wonder that at the Regular Expressions Library site there was no expression for PGN. I made the effort to upload it, maybe other people refine it and make it even better. Here is the link to the uploaded regular expression. The complete regular expression is here:
(?<pgnGame>\s*(?:\[\s*(?<tagName>\w+)\s*"(?<tagValue>[^"]*)"\s*\]\s*)+(?:(?<moveNumber>\d+)(?<moveMarker>\.|\.{3})\s*(?<moveValue>(?:[PNBRQK]?[a-h]?[1-8]?x?[a-h][1-8](?:\=[PNBRQK])?|O(-?O){1,2})[\+#]?(\s*[\!\?]+)?)(?:\s*(?<moveValue2>(?:[PNBRQK]?[a-h]?[1-8]?x?[a-h][1-8](?:\=[PNBRQK])?|O(-?O){1,2})[\+#]?(\s*[\!\?]+)?))?\s*(?:\(\s*(?<variation>(?:(?<varMoveNumber>\d+)(?<varMoveMarker>\.|\.{3})\s*(?<varMoveValue>(?:[PNBRQK]?[a-h]?[1-8]?x?[a-h][1-8](?:\=[PNBRQK])?|O(-?O){1,2})[\+#]?(\s*[\!\?]+)?)(?:\s*(?<varMoveValue2>(?:[PNBRQK]?[a-h]?[1-8]?x?[a-h][1-8](?:\=[PNBRQK])?|O(-?O){1,2})[\+#]?(\s*[\!\?]+)?))?\s*(?:\((?<varVariation>.*)\)\s*)?(?:\{(?<varComment>[^\}]*?)\}\s*)?)*)\s*\)\s*)*(?:\{(?<comment>[^\}]*?)\}\s*)?)*(?<endMarker>1\-?0|0\-?1|1/2\-?1/2|\*)?\s*)


Note: the flavour of the regular expression above is .Net. Javascript does not support named tags, the things between the angle brackets, so if you want to make it work for js, remove ?<name> constructs from it.

Now to work on the actual javascript (ouch!)

Update: I took my glorious regular expression and used it in a javascript code only to find out that groups in Javascript do not act like collections of found items, but only the last match. In other words, if you match 'abc' with (.)* (match as many characters in a row, and capture each character in part) you will get an array that contains 'abc' as the first item and 'c' as the second. That's insane!

Update: As per Matty's suggestion, I've added the less used RxQ move syntax (I do have a hunch that it is not complete, for example stuff like RxN2, RxNa or RxNa2 might also be accepted, but they are not implemented in the regex). I also removed the need for at least one PGN tag. To avoid false positives you might still want to use the + versus the * notation after the tagName/tagValue construct. The final version is here:

(?<pgnGame>\s*(?:\[\s*(?<tagName>\w+)\s*"(?<tagValue>[^"]*)"\s*\]\s*)*(?:(?<moveNumber>\d+)(?<moveMarker>\.|\.{3})\s*(?<moveValue>(?:[PNBRQK]?[a-h]?[1-8]?x?(?:[a-h][1-8]|[NBRQK])(?:\=[PNBRQK])?|O(-?O){1,2})[\+#]?(\s*[\!\?]+)?)(?:\s*(?<moveValue2>(?:[PNBRQK]?[a-h]?[1-8]?x?(?:[a-h][1-8]|[NBRQK])(?:\=[PNBRQK])?|O(-?O){1,2})[\+#]?(\s*[\!\?]+)?))?\s*(?:\(\s*(?<variation>(?:(?<varMoveNumber>\d+)(?<varMoveMarker>\.|\.{3})\s*(?<varMoveValue>(?:[PNBRQK]?[a-h]?[1-8]?x?(?:[a-h][1-8]|[NBRQK])(?:\=[PNBRQK])?|O(-?O){1,2})[\+#]?(\s*[\!\?]+)?)(?:\s*(?<varMoveValue2>(?:[PNBRQK]?[a-h]?[1-8]?x?(?:[a-h][1-8]|[NBRQK])(?:\=[PNBRQK])?|O(-?O){1,2})[\+#]?(\s*[\!\?]+)?))?\s*(?:\((?<varVariation>.*)\)\s*)?(?:\{(?<varComment>[^\}]*?)\}\s*)?)*)\s*\)\s*)*(?:\{(?<comment>[^\}]*?)\}\s*)?)*(?<endMarker>1\-?0|0\-?1|1/2\-?1/2|\*)?\s*)

The Regexlib version has also been updated in a comment (I don't know how - or if it is possible - to edit the original).

Saturday 24 September 2011

Wireless news

There is this childish game called "cordless phone", which funny enough is older than any possible concept of wireless telephony, where in a large group of people a message is sent to someone else by whispering it to your neighbour. Since humans are not network routers, small mistakes creep up in the message as it is copied and resent (hmm, there should be a genetic reference here somewhere as well).

The point is that, given enough people with their own imperfections and/or agendas, a message gets distorted as the number of middle men increases. It also happens in the world of news. Some news company invests in news by paying investigative reporters. The news is created by a human interpreting things from eye witness accounts to scientific papers, but then it is reported by other news agencies, where the original information is not the main source, but the previous news report. Then marketing shows its ugly head, as the titles need to be shockier, more impressive, forcing the hapless reader to open that link, pick up that paper, etc. Occasionally there are translations errors, but mostly it is about idiots who don't and can't understand what they are reporting on, so the original message gets massacred!

So here is one of the news of today, re-reported by Romanian media, after translation and obfuscation and marketization (and retranslation by me, sorry): "Einstein was wrong? A particle that is travelling at more than the speed of light has been discovered". In the body, written a little better, "Elementary subatomic particle" got translated as "Elementary particle of matter". Dear "science" reporters, the neutrino is not a particle that needed discovering and it is not part of normal matter, with which it interacts very little. What is new is just the strange behaviour of the faster than light travel, which is only hinted by some data that may be or not be correct and refuted by some other, like supernova explosions, information that you haven't even bothered to copy paste into your article. And, as if this was not enough, the comments of the readers, kind of like myself ranting here probably, are making the reporter seem brilliant in comparison.

Is there a solution? Not really. People should try to find the original source of messages as much as possible, or at least a reporting source that is professional enough to not skew the information too much when summarizing it for the general public. A technical solution could work that would analyse news reports, group them per topic, then remove copies and translations, red flag emotional language or hidden divergent messages and ignore the titles altogether, maybe generate new ones. And while I know this is possible to do, it would be very difficult (but possibly rewarding) as software goes. One thing is for certain: reading the titles and assuming that they correctly summarize the complete articles is a terrible mistake, alas, one that is very common.

Tuesday 20 September 2011

T-SQL: Determining the byte size of a text in a specific encoding

There was this FTP surrogate program that used SQL as a filesystem. I needed to store the size of the file (which was an HTML template and was stored as NTEXT) in the row where the content was stored. The problem is that the size of a text in a Microsoft SQL Server NTEXT column is about two bytes per character, while the actual size of the content, stored web like in UTF8, was different to almost half.

I thought that there must be an easy way to compute it, trying to cast the string to TEXT then using LEN, trying DATALENGTH, BINARY, etc. Nothing worked. In the end I made my own function, because the size of a string in UTF8 is documented on the Wikipedia page of that encoding: 1 byte for ASCII characters (character code<128), 2 bytes for less than 2048, 3 for 65536 and 4 for the rest. So here is the sql function that computes the size in UTF8:

CREATE FUNCTION [fn_UTF8Size]
(
@text NVARCHAR(max)
)
RETURNS INT
WITH SCHEMABINDING
AS

BEGIN
DECLARE @i INT=1
DECLARE @size INT=0
DECLARE @val INT
WHILE (@i<=LEN(@text))
BEGIN

SET @val=UNICODE(SUBSTRING(@text,@i,1))

SET @size=@size+
CASE
WHEN @val<128 THEN 1
WHEN @val<2048 THEN 2
WHEN @val<65536 THEN 3
ELSE 4
END
SET @i=@i+1

END

RETURN @size
END


A similar approach would work for any other encoding.

Paragraphs containing block elements in XHTML

I was updating the content of a span element via AJAX when I noticed that the content was duplicated. It was no rocket science: get element, replace content with the same thing that was rendered when the page was first loaded, just with updated values. How could it be duplicated?

Investigating the DOM in the browser (any browser, btw) I've noticed something strange: When the page was first loaded, the content was next to the container element, not inside it. I've looked at the page source, only to see that it was, by all counts, correct. It looked something like this:
<p><div><table>...</table></div></p>
. The DOM would show the div inside the paragraph element and the table as the sibling of the paragraph element. The behavior was fixed if the page DOCTYPE was changed from XHTML into something else.

It appears that block elements should not be inside layout elements, like p. The browsers are attempting to "fix" this problem and so they change the DOM, assuming that if a table starts inside the paragraph, then you must have forgotten to close the paragraph. If I was adding it via ajax, the browser did not seem to want to fix the content in any way, as I was manipulating the DOM directly and there was no parsing phase.

Saturday 17 September 2011

State of the Union

I have been reviewing my blog posts for the last few months and I noticed a troubling trend: a lot more social commentary and hobby related stuff than actual tech work. Check out this statistic of posts in the last three months:
  • TV and Movie: 5
  • Books: 6
  • Personal or hobby: 6
  • Social commentary: 1
  • Tech: 8
8 is marginally more than 6, but split them between misc and programming and you get 18 misc for 10 programming (with some overlapping). And consider that two of the tech posts were attempts to fix something that did not work so well.

What does this mean? Do I not learn new stuff at work? Am I not interested in tech work anymore? Am I working too much and not having time to blog? Well, it is a bit of all. I am interested in tech work, but right now I am fighting to adapt to the new job. I am learning new stuff, but that is mostly office related than new frontiers of programming. And I am a bit tired as well.

I have been thinking of cool tech stuff to share with you at least in this post, but I could find none. I am reading a lot of blogs with new information about stuff ranging from Windows 8, .Net 5, the future of C# and Visual Studio to videos of Vesta, things that verge on proving the dark matter model is wrong and amazing BIOS rootkits, but that is not what I am doing.

So let me summarize the technical state of my work so far:
  • Scrum - my workplace uses Scrum as a development practice and invests a lot in maintaining the quality of its implementation. I've learned a lot about the advantages, but also the disadvantages of the practice (there is nothing as annoying as an Outlook alert that you need to do the daily scrum meeting when you are concentrated on a task)
  • Visual Basic - as the original application that was bought by my employing company 5 years ago was written in Visual Basic, large portions of it are still VB. That only proves my point that refactoring code should be a priority, not a nice to have option. I wonder how many developing hours, research hours and hair roots could have been saved if the company would have invested in moving the application to a readable and canonical code form. I also wonder if the guy that invented Visual Basic is now burning in hell, as so many devs with whom I've talked about VB seem to want.
  • Visual Basic - it just deserves two bullet points, for the bullet reason only at least. Also, try converting C# generic and lambda expression code to Visual Basic. Hilarious!
  • Computing power - I am now working on a laptop that has a Quad Core I7 processor, 8Gb of RAM and a Solid State Drive. And I still want it 10 times faster. It seems to me that computing power is only keeping up with the size of the software projects and the complexity of the tools used to develop them, so that the total compile time for a project remains constant. Also, if for some reason the company issues you with a computer powerful enough to break the constant, they also need to enforce drive encryption as to compensate.
  • Continuous Integration and Unit Testing - it gives one a good feeling of comfort to know that after "it works on my machine", the source control server can compile, test and run the software successfully (while you are working at something else, no less).
  • Software Patterns - there are people who can think and visualize software patterns. They can architect any piece of code and make it really neat. However, it now seems to me that an over-architected software is just as hard to read and follow as a non-architected one. Fortunately for me, my colleagues are more the smart "let's make it work" type


That is about it. No magical silver bullet practices, no amazing software, no technological edge code, just plain software shop work.

Tuesday 13 September 2011

Entourage ended

The main characters
As an avid viewer of TV series and movies alike, I am always discussing the latest shows with my friends and I have been surprised to notice that not many knew about Entourage. I consider it a shame, as this series is exactly what a TV show needs to be and so few actually manage to do what it does.

Entourage is the story of a young talented actor who rises from anonymity with the help of three childhood friends. They are practically brothers and, even if he is the only one of them who "made it", they still live together and share everything, while navigating the weird world of Hollywood. The format of the show is short half an hour episodes that never leave you hanging when they end and that, for me at least, always provide a good feeling. I am not talking about silly ha-ha comedy here, I am talking about a lightweight dramedy that makes you smile. At the end of an episode you don't want more, you feel content, and you only begin to crave more when that contentment wears off. This is what today's media shows have forgotten how to do!

I was a bit sad to see the eighth and last season of the show end with its eighth episode a few days ago. I really wanted more of this and now that Entourage is no more, I know it would be hard to find a show that would bring the same peace of mind after each episode. And you haven't heard or tried Entourage, you should. Good show!

The Sparrow (and a bit of Children of God), by Mary Doria Russell

Book coverThe Sparrow is, in my opinion, a good example of politics in literature, as it won several prestigious prizes, but is, in my own view, slightly above average. To quickly summarize the book, it's a Jesuit meets New World story, only the plot is set in the future and the new world is another planet.

Maybe I am just bitter because after tedious chapters about the relationships between the characters, the sci-fi was minimal and outdated. In fact, the author herself admitted that the kind of story she wanted to write could not be set on Earth because there are no new worlds here; this leads me to believe that the sci-fi was incidental and it bloody feels that way, too.

I will give credit where it is due, though. The woman documented herself well and presented interesting characters in great detail. Also the story itself is pretty solid, albeit a bit boring and focused too much on the religious. Other than that, it felt like a 1960's book. I was shocked to see that it was published in 1996. Some of the technical details described in the book were outdated even then.

About the plot, a discreet mission to a nearby planet is organized by the Jesuits, because all the other players in the field like the United Nation are too bureaucratic and talk about it more than actually doing something. The story starts with the return of the only survivor, priest Emilio Sandoz, and then the book continues with back and forth lines of now and then. The ending felt rushed and a bit anticlimactic. Maybe I was just not in an empathetic mood and couldn't care less about the religious and cultural sensitivities of the people involved and from all the characters, only Jimmy, the tall technical guy, felt agreeable. Who would have guessed? :)

Bottom line: I will not read the sequel book and that should say a lot. The book is not bad, though, and I may be berating it too much. A thing is certain, this is at the lower edge of the sci-fi scale, regardless of writing quality.

Update: In lack of a good book to read, I started on Children of God, the sequel to The Sparrow. Russell's talent for tedium, futile philosophy and rape fantasies reaches new heights in this book, so much that I just couldn't finish it. I usually finish watching movies and reading books even if they are bad, just for the experiential value alone, but I couldn't do it with this one. You have the same pointless priest, wallowing in self pity and then pathetically letting himself be used again, all the time asserting that he won't. And just so the reader would have no thoughts that the man could find a way out, a chapter of one of his kidnappers getting old on Rakhat, smack in the middle of the book, erases that possibility. What would be the point of reading on? Just for the ridiculous discussions about different Gods between random people? No. Just avoid this one.

Saturday 3 September 2011

Me playing black against my Nokia cellphone

Here is another chess game, which is not really remarkable as a chess game, but as my perception of it was. I thought I played on par with the cell phone up until the end where I usually falter and this time the cell did. After ChessMaster's analysis, I've realised that the AI made huge mistakes, as have I. I chose to play black because I usually play white and whenever I play as black I am at a loss at what to do, so the few games I've played lately with my cell or PDA were as black.



Also, in this post I am using a new style of annotation. ChessMaster saves PGN files in two ways: with analysis or with auto annotation, not both. I find the auto annotations very helpful when they explain how the game could have played, but not so helpful when I move a piece and it says I moved a piece or some other obvious thing like that. The analysis is more cryptic, but very helpful in understanding what the computer thought. Therefore I took the annotations I found useful and added them to the analysis file. I hope this is more helpful for the reader.



At the end you will see me play really strange, and that is because I was using my queen as a rook only while checkmating. The uncommented variation is the short end.



Well, I just removed the variation and the pointless mate. Just imagine the cell phone gained consciousness and resigned after I queened the pawn :) Also, if you see the post loading really slow, you should wait for it to end. I will try to optimize the chess board plugin when I have the time.





[Event "30/8/2011 9:10:18 am"]

[Site ""]

[Date "2011.8.30"]

[Round ""]

[White "Nokia Easy5"]

[Black "Siderite"]

[TimeControl "-"]

[Result "0-1"]

[ECO " "]



1.e4

{Book.

B00 King's Pawn Opening. The King's Pawn opening move is both popular and logical. It controls the center, opens lines

for both the Queen and the Bishop, and usually leads to an open game in which tactics, rather than slow maneuvering,

predominates.}

1...e5

{Book.

C20 King's Pawn Game. Black responds symmetrically, making a direct challenge to the central squares.}

2.Bc4

{Score: 0.10 2...Nf6 3.Nc3 Nc6 4.Nf3 Nxe4 5.O-O Be7 6.Nxe4 d5 7.d4 O-O 8.Be3 dxc4 9.dxe5 (Time = 1:04, Depth = 2/12)

C23 Bishop's Opening. The Bishop's opening is an attempt to capitalize on the inherent weakness of f7. But centuries of

analysis have shown that this natural move is no stronger when played earlier than later.}

2...b6

{Score: 0.91 3.d4 Bb7 4.dxe5 Bxe4 5.Nf3 Nc6 6.O-O Bb4 7.Nbd2 Bf5 8.Re1 (Time = 0:42, Depth = 1/11)

Out of Opening Book. c6 would have been in the Bishop's Opening opening line. Slightly better is

Nf6.

}

3.Nf3

{Score: 0.63 3...Nc6 4.O-O Bb7 5.Nc3 Bc5 6.d4 exd4 7.Nd5 Nge7 8.Bf4 d6 (Time = 0:42, Depth = 1/11)}

3...Nc6

{Score: 0.69 4.d4 exd4 5.O-O Bb7 6.c3 Nf6 7.e5 d5 8.Bb5 Ne4 9.cxd4 (Time = 0:38, Depth = 1/11)}

4.Nc3

{Score: 0.57 4...Bc5 5.O-O Bb7 6.d3 Nf6 7.a3 O-O 8.b4 Bd6 9.Be3 a6 (Time = 0:38, Depth = 1/11)}

4...Bb7

{Score: 0.54 5.d3 Bc5 6.Ng5 Nh6 7.Qh5 Qf6 8.Nf3 Qg6 9.Qxg6 hxg6 10.O-O (Time = 0:43, Depth = 1/11)}

5.d4

{Score: 0.52 5...exd4 6.Nxd4 Ne5 7.Bd5 c6 8.Bb3 Bc5 9.Nf5 d5 10.exd5 Kf8 (Time = 0:39, Depth = 1/11)}

5...d6

{Score: 1.07 6.O-O a6 7.dxe5 dxe5 8.Bg5 Nge7 9.Qxd8+ Rxd8 10.Rad1 h6 11.Nd5 Rd7 (Time = 0:46, Depth = 1/11)}

6.Ng5

{Score: 1.03 6...Nh6 7.d5 Nd4 8.Nf3 Nxf3+ 9.Qxf3 a6 10.O-O b5 11.Bd3 Be7 12.Bxh6 gxh6 13.Qf5 (Time = 0:29, Depth = 2/12)

}

6...Nh6

{Score: 0.92 7.d5 Ne7 8.Bb5+ c6 9.dxc6 Nxc6 10.Nf3 Ng8 11.O-O a6 12.Bg5 Nf6 13.Bc4 (Time = 0:59, Depth = 2/12)}

7.dxe5

{Score: 0.65 7...Nxe5 8.Be2 Be7 9.f4 Bxg5 10.fxg5 Ng8 11.O-O Ne7 12.Bf4 N7g6 13.Bb5+ c6 14.Bxe5 Nxe5 (Time = 1:00,

Depth = 2/12)}

7...dxe5

{Score: 0.73 8.Be3 Bb4 9.Qh5 O-O 10.O-O Bxc3 11.bxc3 Qf6 12.Rad1 Rad8 13.Nf3 Rd6 (Time = 0:39, Depth = 1/11)}

8.Qh5

{Score: 0.81 8...g6 9.Qh3 Qc8 10.Qf3 f5 11.Ne6 Nd4 12.Nxd4 exd4 13.Nd5 Nf7 14.O-O Ne5 (Time = 1:02, Depth = 2/12)}

8...g6

{Score: 0.81 9.Qh3 Qc8 10.Qf3 f5 11.Ne6 Nd4 12.Nxd4 exd4 13.Nd5 Nf7 14.O-O Ne5 (Time = 0:49, Depth = 1/11)}

9.Qd1

{Score: -0.15 9...Qxd1+ 10.Nxd1 Nd4 11.Ne3 Be7 12.h4 b5 13.Bd3 O-O-O 14.c3 Nb3 15.axb3 Rxd3 16.Rxa7 (Time = 0:57, Depth

= 2/12)

Slightly better is Qh3.}

9...Bg7

{Score: 0.49 10.O-O O-O 11.Nf3 Ng4 12.Bg5 Bf6 13.Bd2 Bg7 14.Qe2 a6 15.Rad1 Nb4 (Time = 0:58, Depth = 2/12)}

10.Nd5

{Score: 0.20 10...Na5 11.Bd3 O-O 12.O-O Nf5 13.Nf3 Nd6 14.b3 Nc6 15.Bg5 f6 (Time = 1:42, Depth = 1/11)}

10...O-O

{Score: 0.18 11.Nf3 Qd6 12.Bg5 Kh8 13.O-O f5 14.Nc3 Qb4 15.Qd3 Qxb2 (Time = 0:39, Depth = 1/10)}

11.c3

{Score: -0.27 11...Na5 12.Bf1 Nf5 13.b4 h6 14.Nf3 Bxd5 15.exd5 e4 16.Nd4 Nxd4 17.cxd4 Nb7 (Time = 2:17, Depth = 2/13)}

11...Ne7

{Score: 0.24 12.Nxe7+ Qxe7 13.O-O c6 14.Be3 Rad8 15.Qe2 b5 16.Bb3 Kh8 17.Rad1 Rfe8 18.Nf3 (Time = 0:49, Depth = 2/12)

}

12.O-O

{Score: 0.06 12...Nxd5 13.Bxd5 Bxd5 14.exd5 Qd7 15.Qd3 c6 16.d6 f5 17.Qc4+ Nf7 18.Ne6 Rfe8 (Time = 0:58, Depth = 2/12)}



12...Nxd5

{Score: 0.01 13.Bxd5 Bxd5 14.exd5 Qd7 15.c4 Nf5 16.Re1 c6 17.Nf3 Rae8 18.Bg5 e4 (Time = 0:41, Depth = 2/12)}

13.Bxd5

{Score: 0.01 13...Bxd5 14.exd5 Nf5 15.Ne4 Nd6 16.f3 a6 17.Qe2 h6 18.Be3 f5 19.Nxd6 Qxd6 (Time = 0:58, Depth = 2/13)}

13...Bxd5

{Score: 0.01 14.exd5 Nf5 15.Ne4 Nd6 16.f3 a6 17.Qe2 h6 18.Be3 f5 19.Nxd6 Qxd6 (Time = 0:49, Depth = 2/12)}

14.Qxd5

{Score: -0.02 14...Qxd5 15.exd5 Nf5 16.Ne4 Nd6 17.Nxd6 cxd6 18.b3 Rfc8 19.Bd2 b5 20.Rac1 Rab8 21.c4 bxc4 22.Rxc4 Rxc4

23.bxc4 (Time = 0:57, Depth = 2/13)}

14...Qe7

{Score: 0.39 15.h3 Rad8 16.Qb3 c5 17.Be3 Qc7 18.Qa4 Qd7 19.Qxd7 Rxd7 20.Nf3 Rfd8 21.Bxh6 Bxh6 22.Nxe5 (Time = 0:58,

Depth = 2/12)}

15.Re1

{Score: 0.16 15...Rad8 16.Qb5 Rd6 17.Nf3 Rfd8 18.Qb3 Ng4 19.Bg5 Bf6 20.Bxf6 Nxf6 21.Qb5 (Time = 0:56, Depth = 2/12)}

15...Rad8

{Score: 0.15 16.Qb5 Rd6 17.Nf3 Rfd8 18.Be3 c5 19.Qb3 Ng4 20.Bg5 Bf6 21.h4 Qd7 (Time = 1:01, Depth = 2/12)}

16.Qb7

{Score: 0.14 16...a5 17.Nf3 Qd6 18.c4 Ng4 19.Qd5 Qb4 20.Qc6 Rd3 21.a3 Qd6 22.Qa4 (Time = 0:54, Depth = 2/12)}

16...a5

{Score: 0.13 17.Nf3 Qd6 18.Qa6 f6 19.Bxh6 Bxh6 20.Qc4+ Kh8 21.Qb3 Qc6 22.Rad1 Rxd1 23.Qxd1 Re8 (Time = 0:50, Depth =

2/12)}

17.h3

{Score: -0.12 17...f5 18.Qc6 f4 19.Qe6+ Rf7 20.Qxe7 Rxe7 21.b3 Nf7 22.Nf3 c6 23.Bb2 Red7 24.c4 b5 (Time = 1:06, Depth =

2/13)}

17...Rb8

{Score: 0.47 18.Qd5 Rbd8 19.Qb3 Kh8 20.Be3 f5 21.Ne6 f4 22.Nxf8 fxe3 23.Ne6 exf2+ 24.Kxf2 (Time = 0:35, Depth = 2/12)

}

18.Qc6

{Score: 0.34 18...Rfd8 19.Be3 Qd7 20.Qc4 b5 21.Qe2 f5 22.Rad1 Qc6 23.Qc2 a4 24.b3 Ra8 (Time = 1:00, Depth = 2/13)}

18...Rfd8

{Score: 0.34 19.Be3 Qd7 20.Qc4 b5 21.Qe2 f5 22.Rad1 Qc6 23.Qc2 a4 24.b3 b4 (Time = 1:07, Depth = 2/12)

Seizes the open file.}

19.Qb5

{Score: 0.06 19...f6 20.Nf3 Nf7 21.Be3 Qd7 22.Qb3 a4 23.Qc2 Qc6 24.b3 Nd6 25.Bd2 Ra8 26.Rad1 axb3 27.axb3 (Time = 1:31,

Depth = 2/14)}

19...Kh8

{Score: 0.34 20.Nf3 f6 21.Be3 Nf7 22.Rad1 Nd6 23.Qc6 h6 24.b3 Kh7 25.Qa4 c5 (Time = 0:59, Depth = 2/12)}

20.Be3

{Score: 0.17 20...f5 21.exf5 Nxf5 22.Bf4 Rf8 23.Rxe5 Bxe5 24.Bxe5+ Ng7 25.Re1 Rf5 26.Nf3 Rd8 27.Qc4 (Time = 0:58, Depth

= 2/13)}

20...Ng8

{Score: 0.58 21.Qb3 Rf8 22.Nf3 h6 23.Qb5 Rfd8 24.Rad1 Qe6 25.c4 f5 26.exf5 Qxf5 (Time = 0:40, Depth = 2/12)}

21.Qc4

{Score: 0.59 21...Rf8 22.Nf3 c5 23.Rad1 Nf6 24.Bg5 b5 25.Qe2 Rfc8 26.Bxf6 Bxf6 27.Qd3 (Time = 0:46, Depth = 2/12)}

21...Rf8

{Score: 0.57 22.Rad1 Nf6 23.Nf3 c5 24.Bg5 b5 25.Qe2 Rfc8 26.Rd5 Qe6 27.Rxe5 Qxa2 (Time = 1:01, Depth = 2/12)}

22.Rad1

{Score: 0.53 22...Bh6 23.h4 c5 24.Qb5 Nf6 25.c4 Qc7 26.Ne6 fxe6 27.Bxh6 Rfd8 28.f3 (Time = 0:59, Depth = 2/12)

Takes control of the open file.}

22...Nf6

{Score: 0.57 23.f4 Nh5 24.fxe5 Bxe5 25.Rf1 f6 26.Ne6 Rfe8 27.Nd4 Bxd4 28.Bxd4 (Time = 0:58, Depth = 1/11)}

23.Rf1

{Score: 0.28 23...h6 24.Nf3 c5 25.Rfe1 Kg8 26.Qb3 Rfe8 27.c4 Nxe4 28.Bxh6 Nxf2 29.Kxf2 Bxh6 30.Rxe5 (Time = 1:03, Depth

= 2/13)}

23...h6

{Score: 0.28 24.Nf3 c5 25.Rfe1 Kg8 26.Qb3 Rfe8 27.c4 Nxe4 28.Bxh6 Nxf2 29.Kxf2 Bxh6 30.Rxe5 (Time = 0:36, Depth =

2/12)}

24.Nf3

{Score: 0.24 24...c5 25.Rfe1 Rfe8 26.Qb3 Qb7 27.Qc2 Kg8 28.Bc1 Qc7 29.c4 Rbd8 30.Be3 (Time = 0:59, Depth = 2/12)}

24...Kh7

{Score: 0.33 25.b4 Qe6 26.Qxe6 fxe6 27.bxa5 Nxe4 28.axb6 Rf7 29.Rfe1 cxb6 30.Bd2 Nxd2 31.Rxd2 (Time = 0:49, Depth =

1/11)}

25.Qc6

{Score: 0.33 25...Rfd8 26.c4 Rd6 27.Rxd6 Qxd6 28.Qxd6 cxd6 29.Nd2 b5 30.f3 bxc4 31.Nxc4 d5 32.Ba7 Rb7 (Time = 0:36,

Depth = 2/12)}

25...Rbd8

{Score: 0.54 26.c4 Rd6 27.Rxd6 Qxd6 28.Qxd6 cxd6 29.Nd2 Nd7 30.Nb1 f5 31.exf5 gxf5 32.Rd1 f4 (Time = 0:59, Depth =

2/12)}

26.a4

{Score: 0.33 26...Rc8 27.Rd2 Rfe8 28.Rfd1 Qe6 29.Qxe6 Rxe6 30.Rd8 Rxd8 31.Rxd8 Re7 32.Nd2 Re8 (Time = 1:11, Depth =

2/12)}

26...Rd6

{Score: 0.51 27.Rxd6 Qxd6 28.Qxd6 cxd6 29.Nd2 Rb8 30.Rd1 b5 31.axb5 Rxb5 32.b3 d5 33.exd5 Nxd5 34.c4 Nxe3 35.fxe3 Rb7

36.c5 (Time = 1:01, Depth = 3/15)}

27.Qc4

{Score: 0.12 27...Kg8 28.Rfe1 Ra8 29.Rxd6 Qxd6 30.Rb1 Ra7 31.b3 c5 32.b4 axb4 33.cxb4 (Time = 0:37, Depth = 2/12)}

27...Rfd8

{Score: 0.17 28.Rxd6 Qxd6 29.b4 Ra8 30.b5 Kg8 31.Nd2 Nh5 32.Qd5 Qxd5 33.exd5 f5 34.Nf3 Rd8 (Time = 0:48, Depth =

2/13)}

28.Rxd6

{Score: 0.15 28...Qxd6 29.Qxf7 Nxe4 30.Qc4 Nf6 31.Bg5 Rf8 32.Bxf6 Bxf6 33.Re1 Bg7 34.b4 Rf4 (Time = 0:50, Depth =

2/13)}

28...Rxd6

{Score: 0.26 29.Re1 Ne8 30.b4 Qe6 31.Qxe6 Rxe6 32.bxa5 bxa5 33.Rd1 Nd6 34.Nd2 f5 35.f3 Re7 (Time = 0:58, Depth =

2/13)}

29.b3

{Score: 0.10 29...Qe6 30.Qxc7 Nxe4 31.c4 Nc3 32.Re1 e4 33.Bf4 Rd7 34.Qb8 Rd3 35.Bg3 Qc6 (Time = 0:54, Depth = 2/13)}

29...Ne8

{Score: 0.25 30.b4 Qe6 31.Qxe6 Rxe6 32.bxa5 bxa5 33.Rd1 Nd6 34.Nd2 f5 35.f3 Re8 36.Bc5 Rd8 (Time = 0:59, Depth =

2/13)}

30.b4

{Score: 0.25 30...Qd7 31.bxa5 bxa5 32.Qb3 Nf6 33.Nxe5 Qe6 34.Qxe6 Rxe6 35.Nxf7 Rxe4 36.Bd4 Nd5 37.Bxg7 Kxg7 (Time =

1:03, Depth = 2/13)}

30...axb4

{Score: 0.45 31.cxb4 f5 32.a5 fxe4 33.Qxe4 Nf6 34.Qxe5 Qxe5 35.Nxe5 Nd5 36.Nc4 Rc6 37.Rc1 Nc3 38.Nd2 (Time = 0:45,

Depth = 2/12)}

31.cxb4

{Score: 0.62 31...f5 32.a5 fxe4 33.Nd2 Qd7 34.Nxe4 Rc6 35.Qb5 Nf6 36.axb6 cxb6 37.Qxe5 Nxe4 38.Qxe4 (Time = 1:02,

Depth = 2/12)}

31...Bf8

{Score: 1.42 32.a5 bxa5 33.bxa5 Rd8 34.a6 c5 35.Rc1 Qc7 36.Qc3 Ra8 37.Ra1 f6 (Time = 0:38, Depth = 1/11)

Slightly better is f5.}

32.a5

{Score: 1.42 32...bxa5 33.bxa5 Rd8 34.a6 c5 35.Rc1 Qc7 36.Qc3 Ra8 37.Ra1 f6 (Time = 0:37, Depth = 1/10)}

32...c5

{Score: 3.57 33.bxc5 bxc5 34.Bxc5 Qc7 35.Qb5 Bg7 36.Bxd6 Nxd6 37.Qd5 f5 38.exf5 Nxf5 39.Re1 Ne7 (Time = 0:44, Depth =

2/13)

Leads to 33.bxc5 bxc5 34.Bxc5 Qc7 35.Qb5 Bg7 36.Bxd6 Nxd6 37.Qd5 f5 38.exf5 Nxf5 39.Re1 Ne7, which wins a bishop and

two pawns for a rook and three pawns. Better is bxa5, leading to 33.bxa5 Rd8 34.a6 c5 35.Rc1 Qc7 36.Qc3 Ra8 37.Ra1 f6, which

wins a pawn for a pawn. This was black's most serious miscue, but black was able to stay close and eventually

mated.

}

33.bxc5

{Score: 3.92 33...bxc5 34.Bxc5 Qc7 35.Qb5 Nf6 36.a6 Nxe4 37.a7 Rd8 38.Bb6 Qb7 39.Bxd8 Qxb5 40.a8=Q Nc3 (Time = 1:09,

Depth = 3/14)}

33...bxc5

{Score: 4.13 34.Bxc5 Kg7 35.Bxd6 Nxd6 36.Qc6 f5 37.exf5 gxf5 38.a6 e4 39.Nd4 Kh8 40.Qd5 Qf6 (Time = 1:04, Depth =

3/14)}

34.Qxc5

{Score: 1.88 34...Nf6 35.Qb5 Nxe4 36.a6 Nc3 37.Qa5 Ne2+ 38.Kh1 Qd8 39.Qxd8 Rxd8 40.Nxe5 f6 41.Re1 (Time = 1:01, Depth

= 2/12)

Leads to 34...Nf6 35.Qb5 Nxe4 36.a6 Nc3 37.Qa5 Ne2+ 38.Kh1 Qd8 39.Qxd8 Rxd8 40.Nxe5 f6 41.Re1, which wins a queen and

two pawns for a queen and a pawn. Better is Bxc5, leading to 34...Kg7 35.Bxd6 Nxd6 36.Qc6 f5 37.exf5 gxf5 38.a6 e4 39.Nd4 Kh8

40.Qd5 Qf6, which wins a rook and two pawns for a bishop and a pawn.}

34...Ra6

{Score: 2.27 35.Qxe7 Bxe7 36.Nxe5 Nd6 37.Rc1 Bg5 38.f4 Bh4 39.Rc7 Kg8 40.Bc5 Nxe4 41.Nxf7 Rxa5 42.Nxh6+ Kh8 (Time =

1:00, Depth = 3/14)}

35.Qb5

{Score: 2.06 35...Re6 36.Ra1 Nc7 37.Qc4 Na6 38.Rc1 Qb7 39.Qc8 Qxc8 40.Rxc8 Bb4 41.Bb6 Kg7 (Time = 0:41, Depth = 2/12)}

35...Nc7

{Score: 2.79 36.Qc4 Ra8 37.Rc1 Ne6 38.Nxe5 Bg7 39.Nc6 Qb7 40.e5 Qb2 41.Re1 Rc8 (Time = 0:37, Depth = 2/12)}

36.Qc4

{Score: 2.74 36...Ra8 37.Rc1 Ne6 38.Nxe5 Bg7 39.Nc6 Qb7 40.e5 Nd8 41.Nxd8 Rxd8 42.a6 Qd7 43.Bb6 (Time = 1:22, Depth =

2/13)}

36...Ra8

{Score: 2.74 37.Rc1 Ne6 38.Nxe5 Bg7 39.Nc6 Qb7 40.e5 Nd8 41.Nxd8 Rxd8 42.a6 Qd7 43.Bb6 (Time = 0:58, Depth = 2/12)}

37.Ra1

{Score: 2.32 37...Bg7 38.a6 Ne6 39.Qc6 Qe8 40.Qxe8 Rxe8 41.a7 Nc7 42.Bc5 f6 43.Ra3 f5 (Time = 0:39, Depth = 2/11)}

37...Bg7

{Score: 2.44 38.a6 Qd7 39.a7 Ne6 40.Ra5 Qd1+ 41.Kh2 Qd8 42.Rd5 Qh8 43.Rd7 Rc8 (Time = 0:39, Depth = 2/11)}

38.a6

{Score: 2.40 38...Ne6 39.Qc6 Qd8 40.Bb6 Qf8 41.a7 Nd4 42.Nxd4 exd4 43.Ra5 d3 44.e5 (Time = 0:39, Depth = 2/11)}

38...Qd6

{Score: 2.84 39.a7 Ne6 40.Qb5 Qf8 41.Qb7 Nf4 42.Bxf4 exf4 43.e5 Qe8 44.Qe4 f6 45.e6 (Time = 0:55, Depth = 2/12)}

39.Qxf7

{Score: 2.09 39...Rxa6 40.Rc1 Rc6 41.Rxc6 Qxc6 42.Nxe5 Qxe4 43.Nxg6 Qxg6 44.Qxc7 Qd3 45.Kh2 Qe4 46.Bf4 Qg6 (Time =

0:37, Depth = 2/13)

Slightly better is a7.}

39...Rxa6

{Score: 2.08 40.Rc1 Rc6 41.Rxc6 Qxc6 42.Nxe5 Qxe4 43.Nxg6 Qb1+ 44.Kh2 Qxg6 45.Qxc7 Qe4 46.Bf4 Qe6 47.f3 (Time =

0:38, Depth = 2/13)}

40.Rxa6

{Score: 1.30 40...Nxa6 41.Qd5 Qc7 42.Qb5 Qd6 43.Kh2 Nc7 44.Qc4 Kh8 45.g3 Qd7 46.Kg2 Ne6 (Time = 0:51, Depth = 3/13)

Slightly better is Rc1.}

40...Nxa6

{Score: 1.30 41.Qd5 Qc7 42.Qb5 Nb8 43.Qb4 Nc6 44.Qc4 Qb7 45.Nd2 Nd4 46.Bxd4 exd4 47.f4 (Time = 0:58, Depth = 2/12)}

41.Qd5

{Score: 1.39 41...Qc7 42.Qe6 Qb7 43.Qc4 Nb8 44.g3 Nd7 45.Kg2 Nf6 46.Nd2 Qd7 47.Qc3 Ne8 (Time = 1:31, Depth = 3/13)}

41...Qf6

{Score: 1.41 42.Qb7 Qd6 43.Nd2 Nb8 44.Qd5 Qa6 45.Nc4 Nc6 46.Qf7 Qa4 47.f3 Qa1+ 48.Kh2 Qe1 (Time = 0:55, Depth = 3/13)

}

42.Kf1

{Score: 1.26 42...Nb4 43.Qc4 Qd6 44.g3 Nd3 45.Nd2 Qa3 46.Nb1 Qd6 47.Nc3 Nb2 48.Qd5 Qa6+ 49.Nb5 Qc8 (Time = 0:46, Depth

= 3/14)}

42...Nb4

{Score: 1.26 43.Qc4 Qd6 44.g3 Nd3 45.Nd2 Qa3 46.Qb3 Qa6 47.Kg2 Nc1 48.Qc4 Qxc4 49.Nxc4 Nd3 50.f4 (Time = 0:59, Depth

= 3/14)}

43.Qd7

{Score: 1.16 43...Qc6 44.Qxc6 Nxc6 45.Ke2 Kg8 46.Kd3 Kf7 47.Kc4 Ke6 48.Kb5 Nb8 49.Nh4 Kf7 50.Bc5 (Time = 0:37, Depth =

3/13)}

43...Nc2

{Score: 2.47 44.Bxh6 Kxh6 45.Qd2+ Kh7 46.Qxc2 Qa6+ 47.Kg1 Qb6 48.g3 Bf6 49.Qc4 Kh6 50.Qd5 Qc7 51.Kg2 Qc3 52.Qd6 (Time

= 0:37, Depth = 3/14)

Slightly better is Qc6.}

44.Bxh6

{Score: 2.50 44...Kxh6 45.Qd2+ Kh7 46.Qxc2 Qa6+ 47.Kg1 Qb6 48.g3 Bf6 49.Qc4 Kg7 50.Kg2 Qd6 51.Qd5 Qxd5 52.exd5 e4

53.Nd2 (Time = 1:02, Depth = 4/15)}

44...Kxh6

{Score: 2.56 45.Qd2+ Kh7 46.Qxc2 Qa6+ 47.Kg1 Qb6 48.g3 Bf6 49.Qd3 Qc7 50.Qd5 Kh6 51.Kg2 Qc3 52.Qd6 (Time = 0:23,

Depth = 3/13)}

45.Ke2

{Score: -1.41 45...Nd4+ 46.Nxd4 exd4 47.Qb5 Qe6 48.Qd3 g5 49.Qc2 Qa6+ 50.Qd3 Qa2+ 51.Kf3 Qe6 52.Ke2 Qc6 53.f3 Qc3 (Time

= 0:38, Depth = 3/13)

Leads to 45...Nd4+ 46.Nxd4 exd4 47.Qb5 Qe6 48.Qd3 g5 49.Qc2 Qa6+ 50.Qd3 Qa2+ 51.Kf3 Qe6 52.Ke2 Qc6 53.f3 Qc3, which

wins a knight for a knight. Better is Qd2+, leading to 45...Kh7 46.Qxc2 Qa6+ 47.Kg1 Qb6 48.g3 Bf6 49.Qd3 Qc7 50.Qd5 Kh6 51.Kg2

Qc3 52.Qd6, which wins a knight.}

45...Nd4+

{Score: -1.41 46.Nxd4 exd4 47.Qb5 Qe6 48.Qd3 g5 49.Qc2 Qa6+ 50.Qd3 Qb7 51.f3 Be5 52.Qc4 Qb2+ 53.Kf1 Qa1+ 54.Ke2 Qc3

(Time = 0:40, Depth = 3/14)}

46.Nxd4

{Score: -1.38 46...exd4 47.Qb5 Qe6 48.Qd3 g5 49.Qc2 Qa6+ 50.Qd3 Qa2+ 51.Qd2 Qb1 52.f3 Kg6 53.Qd3 Qa2+ 54.Kf1 (Time =

0:22, Depth = 3/13)}

46...exd4

{Score: -1.34 47.Qb5 Qe6 48.Qd3 g5 49.Kf1 Qc8 50.Ke2 Bf6 51.f3 Qc3 52.Qxc3 dxc3 53.Kd3 Kg6 54.Kc2 (Time = 1:26,

Depth = 5/14)}

47.Qb5

{Score: -1.35 47...Qe6 48.Qb1 g5 49.f3 Bf6 50.Qb4 Qa2+ 51.Kf1 Kg6 52.Qb5 Qd2 53.Qf5+ Kg7 54.Qd7+ Kh6 55.Qc6 Kg6 (Time =

1:14, Depth = 5/14)}

47...Qe6

{Score: -1.38 48.Qb1 Be5 49.Qd3 g5 50.Qa3 Qc4+ 51.Qd3 Qc3 52.Qxc3 dxc3 53.Kd3 Kh5 54.f3 Kg6 55.Kc2 (Time = 0:56,

Depth = 4/13)}

48.Qd5

{Score: -2.44 48...Qa6+ 49.Kf3 Qd3+ 50.Kf4 Qd2+ 51.Kg3 d3 52.h4 Qe2 53.Qg5+ Kh7 54.h5 Qxe4 55.hxg6+ Qxg6 56.Qxg6+ Kxg6

57.Kf3 Bh6 58.g4 d2 59.Ke2 (Time = 1:00, Depth = 4/13)

Slightly better is Qb1.}

48...Qc8

{Score: -1.33 49.Qb3 Be5 50.Qd3 g5 51.f3 Qc6 52.Kf2 Qa4 53.Qe2 Qb3 54.Qa6+ Kg7 55.Qc6 Kf7 (Time = 0:58, Depth = 4/13)

Slightly better is Qa6+.}

49.Qa2

{Score: -1.39 49...Be5 50.Qd2+ g5 51.Qd3 Kg7 52.Qb3 Qa6+ 53.Kf3 Bf6 54.g3 Qf1 55.Qe6 d3 56.Qd7+ Kh6 (Time = 0:39, Depth

= 3/12)}

49...Qc6

{Score: -1.36 50.Qd2+ g5 51.f3 Kh5 52.Kf2 Qc5 53.f4 d3+ 54.Kf3 Qc2 55.g4+ Kg6 56.f5+ Kh6 57.Qe3 Qd1+ 58.Kf2 Bf6 (Time

= 0:55, Depth = 4/13)}

50.Qd2+

{Score: -1.40 50...g5 51.Qd3 Bf6 52.g3 Qc1 53.Kf3 Kg7 54.Kg2 Qc3 55.Qb5 Qc2 56.Qd7+ Kh6 57.Qf5 (Time = 0:30, Depth =

4/13)}

50...Kh7

{Score: -1.18 51.Qd3 Qd6 52.g3 Qe6 53.h4 Qa2+ 54.Kf1 Qa1+ 55.Ke2 Qb2+ 56.Ke1 Qc3+ 57.Ke2 Kg8 58.f4 Kf7 59.e5 Bh6

(Time = 1:04, Depth = 5/14)}

51.Kf3

{Score: -2.85 51...Qc4 52.g4 Qf1 53.Kg3 Qg1+ 54.Kf3 Qh1+ 55.Kg3 Qxe4 56.f3 Qc6 57.Qe2 Qc3 58.h4 d3 (Time = 0:42, Depth

= 4/13)

Pins own pawn at e4. Leads to 51...Qc4 52.g4 Qf1 53.Kg3 Qg1+ 54.Kf3 Qh1+ 55.Kg3 Qxe4 56.f3 Qc6 57.Qe2 Qc3 58.h4 d3,

which loses a pawn. Better is Qd3, leading to 51...Qd6 52.g3 Qe6 53.h4 Qa2+ 54.Kf1 Qa1+ 55.Ke2 Qb2+ 56.Ke1 Qc3+ 57.Ke2 Kg8 58.f4

Kf7 59.e5 Bh6, which does not result in any captures.}

51...Qf6+

{Score: -1.34 52.Ke2 Qe5 53.Qd3 Bf6 54.g3 Qh5+ 55.g4 Qe5 56.Kf3 Kg7 57.Qb3 Qh2 58.Ke2 Qh1 59.Qb7+ Kh6 (Time = 0:58,

Depth = 5/14)

Frees White's pawn at e4 from the pin. Leads to 52.Ke2 Qe5 53.Qd3 Bf6 54.g3 Qh5+ 55.g4 Qe5 56.Kf3 Kg7 57.Qb3 Qh2

58.Ke2 Qh1 59.Qb7+ Kh6, which does not result in any captures. Better is Qc4, leading to 52.g4 Qf1 53.Kg3 Qg1+ 54.Kf3 Qh1+

55.Kg3 Qxe4 56.f3 Qc6 57.Qe2 Qc3 58.h4 d3, which wins a pawn.}

52.Ke2

{Score: -1.33 52...Qe5 53.Qd3 Bf6 54.g3 Qh5+ 55.g4 Qe5 56.Qb1 Qf4 57.Qd3 Kg7 58.Qc2 Bh4 59.f3 (Time = 0:56, Depth =

4/13)}

52...Qh4

{Score: -1.36 53.Qd3 Bf6 54.g4 Qg5 55.Qb3 Bg7 56.Qc4 Qf4 57.Qe6 Qc1 58.h4 Qc2+ 59.Kf3 d3 60.e5 (Time = 1:25, Depth =

5/14)}

53.f3

{Score: -1.45 53...Qg3 54.Kf1 Qe5 55.Ke2 Bf6 56.Qc1 Qg3 57.Qf1 Qg5 58.Kd1 Qe3 59.Qe2 Qc3 (Time = 0:38, Depth = 4/13)}

53...Qg3

{Score: -1.55 54.Kf1 Bf6 55.Qa2 Qd6 56.Qd5 Qb6 57.g4 Qb1+ 58.Kg2 Qb2+ 59.Kf1 Qc1+ 60.Kg2 Qd2+ 61.Kg1 Qe3+ 62.Kg2 Qe2+

63.Kg3 d3 (Time = 1:16, Depth = 5/14)}

54.Kd3

{Score: -1.69 54...Bf6 55.Qe2 Qb8 56.Kc2 Qc7+ 57.Kb1 Bg5 58.Qc2 Qb6+ 59.Ka2 Qa6+ 60.Kb2 Bf4 61.Qb3 Qe2+ 62.Qc2 d3

63.Qxe2 dxe2 (Time = 0:58, Depth = 4/13)}

54...Bh6

{Score: -1.35 55.Qa2 Qd6 56.Qb3 Be3 57.Qb7+ Kh6 58.Qa8 Qb6 59.e5 Kg5 60.h4+ Kxh4 61.Qe4+ Kg3 62.Qg4+ Kf2 63.f4 Qb5+

64.Ke4 Qc6+ 65.Kd3 Qxg2 (Time = 1:33, Depth = 5/14)}

55.Qa2

{Score: -1.39 55...Qd6 56.Qd5 Qb4 57.Qxd4 Qd2+ 58.Kc4 Qxg2 59.Qd7+ Bg7 60.Qg4 Qc2+ 61.Kd5 Qd3+ 62.Ke6 Qd8 63.Qg3 Qb6+

64.Kd7 Bf6 (Time = 1:00, Depth = 4/13)}

55...Bg7

{Score: 0.00 56.Qd2 (Time = 0:30, Depth = 7/16)

Leads to 56.Qd2, which does not result in any captures. Better is Qd6, leading to 56.Qd5 Qb4 57.Qxd4 Qd2+ 58.Kc4 Qxg2

59.Qd7+ Bg7 60.Qg4 Qc2+ 61.Kd5 Qd3+ 62.Ke6 Qd8 63.Qg3 Qb6+ 64.Kd7 Bf6, which wins a pawn for a pawn.}

56.Qd2

{Score: -1.64 56...Bf6 57.Qe2 Qg5 58.Kc2 Qc5+ 59.Kd1 d3 60.Qxd3 Qg1+ 61.Ke2 Qxg2+ 62.Ke3 Kh6 63.Qd1 Bg5+ 64.Kd4 Qxh3

(Time = 1:01, Depth = 4/13)}

56...Qd6

{Score: -1.70 57.Ke2 Bh6 58.Qd3 Qb4 59.g3 Qb2+ 60.Kf1 Be3 61.Qe2 Qc1+ 62.Qe1 Qc4+ 63.Kg2 Qd3 64.Kh1 Qc2 (Time = 0:51,

Depth = 4/13)}

57.Qa2

{Score: -1.75 57...Qc5 58.Qc4 Qa3+ 59.Kc2 d3+ 60.Qxd3 Qb2+ 61.Kd1 Qxg2 62.h4 Qg1+ 63.Ke2 Qh2+ 64.Ke3 Qxh4 65.Qd6 Qe1+

66.Kf4 Qc1+ 67.Kg4 Qc8+ 68.Kg5 (Time = 0:55, Depth = 4/13)}

57...Qb4

{Score: -1.86 58.Ke2 Qb5+ 59.Kf2 Qc5 60.Ke2 Qg5 61.Kd1 d3 62.Qd2 Qb5 63.Ke1 Qb1+ 64.Kf2 Qc2 65.Ke1 Be5 (Time = 1:09,

Depth = 5/14)}

58.Ke2

{Score: -1.86 58...Qb5+ 59.Kf2 Qc5 60.Ke2 Qg5 61.Kd1 d3 62.Qd2 Qb5 63.Ke1 Qb1+ 64.Kf2 Qc2 65.Ke1 Be5 (Time = 0:58,

Depth = 4/13)}

58...Qc3

{Score: -1.33 59.Qd2 Qc5 60.f4 Qh5+ 61.Kf1 Qb5+ 62.Kf2 Qc4 63.g4 Bf8 64.f5 Bb4 65.Qb2 Qc3 66.Qxc3 dxc3 (Time = 0:37,

Depth = 4/13)}

59.Qd2

{Score: -1.29 59...Qb3 60.Qd3 Qb2+ 61.Qd2 Qb5+ 62.Kf2 Qc4 63.Qg5 Qc2+ 64.Kf1 d3 65.Qh4+ Kg8 66.Qd8+ Bf8 67.Qf6 Qc1+

68.Kf2 Bc5+ 69.Kg3 Qe1+ 70.Kg4 (Time = 0:57, Depth = 4/13)}

59...g5

{Score: -0.91 60.Qxg5 d3+ 61.Kf1 Bf6 62.Qf5+ Kg7 63.Qg4+ Kf8 64.Qf5 Ke7 65.Qh7+ Kd6 66.Qh6 Ke6 67.Qg6 Qc1+ 68.Kf2 Ke7

69.Qg8 (Time = 0:49, Depth = 4/13)}

60.Qd3

{Score: -1.27 60...Qc5 61.Qb3 Qc1 62.Qd5 Qe3+ 63.Kf1 d3 64.Qf5+ Kg8 65.Qc8+ Bf8 66.Qe6+ Kh8 67.Qc8 Qc5 68.Qxc5 Bxc5

(Time = 0:44, Depth = 3/12)}

60...Bf6

{Score: -1.05 61.Qxc3 dxc3 62.g3 Be5 63.h4 g4 64.fxg4 Bxg3 65.Kd3 Be5 66.Ke2 Kg6 67.Kd3 Bd4 68.Kc2 Bg7 (Time = 0:47,

Depth = 6/15)}

61.Qxc3

{Score: -0.96 61...dxc3 62.g3 Be5 63.h4 g4 64.fxg4 Bxg3 65.Kd3 Be5 66.Kc2 Bg7 67.Kd3 Kg6 68.Kc2 Bf6 69.Kd3 Kg7 (Time =

0:10, Depth = 10/17)}

61...dxc3

{Score: -0.91 62.g3 Be5 63.h4 g4 64.fxg4 Bxg3 65.Kd3 Be5 66.Kc2 Kg6 67.Kd3 Bd4 68.Kc2 Bf6 69.Kd3 Be5 70.Kc2 Kg7

71.Kd3 Bd4 72.Kc2 (Time = 0:54, Depth = 15/21)}

62.Kd3

{Score: -1.10 62...Be5 63.g3 Bxg3 64.Kxc3 Kg6 65.Kd4 Kf6 66.Kd5 Ke7 67.Kd4 Ke6 68.Ke3 Ke5 69.Kd3 Bf2 70.Ke2 Bc5 71.Kf1

Be7 72.Kf2 Kf4 73.Ke2 (Time = 0:47, Depth = 16/22)}

62...Kg6

{Score: -0.81 63.g3 Bd4 64.f4 Kh6 65.Kc2 Kh5 66.e5 Kg6 67.e6 Kf6 68.fxg5+ Kxe6 69.g4 Ke5 70.Kd3 Kf4 71.g6 Bg7 72.Kc2

Ke4 (Time = 0:49, Depth = 13/19)}

63.g3

{Score: -0.72 63...Bd4 64.f4 Kf7 65.e5 Ke6 66.Kc2 Ke7 67.fxg5 Ke6 68.h4 Kxe5 69.h5 Kf5 70.g6 Kg5 71.g4 Bg7 72.Kd3 Kxg4

(Time = 0:36, Depth = 12/18)}

63...Kh5

{Score: -0.73 64.f4 Bd4 65.Kc2 Kg6 66.Kd3 Kh6 67.Kc2 Kh5 68.Kd3 Kg6 69.Kc2 Kf6 70.Kd3 gxf4 71.gxf4 Ke6 72.h4 Kd7

73.h5 Ke6 (Time = 1:09, Depth = 13/19)}

64.f4

{Score: -0.49 64...Bd4 65.e5 Kg6 66.e6 Kf6 67.f5 Be5 68.g4 Ke7 69.Ke2 Bg7 70.Kd1 Bh8 71.Kc2 Bf6 72.Kd1 Bd4 73.Kc2 (Time

= 0:59, Depth = 12/18)}

64...Kg6

{Score: 0.00 65.e5 Bg7 66.Kxc3 gxf4 67.gxf4 Bh6 68.Kc2 Bxf4 69.Kd3 Bxe5 70.Kc2 Kf7 71.Kd2 Bd4 72.Kd3 Bg7 73.Kd2 Ke7

74.Kd3 (Time = 0:36, Depth = 12/18)}

65.e5

{Score: 0.00 65...Bg7 66.Kxc3 gxf4 67.gxf4 Bh6 68.Kd4 Bxf4 69.e6 Kg7 70.e7 Kf7 71.e8=Q+ Kxe8 72.Kd3 Kf7 73.Kd4 Bh6

74.Kd3 Bg5 75.Kc4 Bh6 (Time = 0:36, Depth = 12/18)}

65...gxf4

{Score: 0.00 66.gxf4 Be7 67.Kxc3 Kf5 68.Kd3 Kxf4 69.Ke2 Kxe5 70.Kd3 Kf5 71.Kd2 Kg6 72.h4 Bxh4 (Time = 0:13, Depth =

12/18)}

66.exf6

{Score: -9.21 66...fxg3 67.f7 Kxf7 68.Ke3 g2 69.Kf2 g1=Q+ 70.Kxg1 Kf6 71.Kf2 c2 72.Kf3 c1=Q 73.Ke4 Qh6 74.Kd4 Qxh3

75.Kc4 Qe6+ 76.Kd4 Qe5+ 77.Kd3 Qd5+ 78.Ke3 Ke5 (Time = 0:59, Depth = 9/15)

Yikes! Leads to 66...fxg3 67.f7 Kxf7 68.Ke3 g2 69.Kf2 g1=Q+ 70.Kxg1 Kf6 71.Kf2 c2 72.Kf3 c1=Q 73.Ke4 Qh6 74.Kd4 Qxh3

75.Kc4 Qe6+ 76.Kd4 Qe5+ 77.Kd3 Qd5+ 78.Ke3 Ke5. Much better is gxf4, leading to 66...Be7 67.Kxc3 Kf5 68.Kd3 Kxf4 69.Ke2 Kxe5

70.Kd3 Kf5 71.Kd2 Kg6 72.h4 Bxh4, which gains a queen and loses a bishop by comparison. This was white's key error. White was

not able to regain the lost ground and was eventually

mated.

}

66...fxg3

{Score: -9.34 67.Ke2 g2 68.Kd3 g1=Q 69.Kxc3 Kxf6 70.Kb4 Ke5 71.Kb5 Qf1+ 72.Kc6 Qxh3 73.Kc7 Qc3+ 74.Kb6 Qd3 75.Kc5

Qd5+ 76.Kb4 (Time = 0:35, Depth = 9/15)}

67.Ke2

{Score: -9.34 67...Kxf6 68.Kf3 c2 69.Kxg3 c1=Q 70.Kf2 Qf4+ 71.Ke2 Ke5 72.Kd3 Qf5+ 73.Kd2 Qxh3 74.Ke2 Qg4+ 75.Kd2 Qd4+

76.Ke2 Qe4+ 77.Kd2 (Time = 0:59, Depth = 8/14)}

67...c2

{Score: -9.00 68.f7 Kxf7 69.Kf3 c1=Q 70.Kxg3 Qg1+ 71.Kf4 Qh2+ 72.Kg4 Qg2+ 73.Kf4 Qxh3 74.Ke4 Qe6+ 75.Kd4 Kf6 (Time =

0:49, Depth = 6/12)}

68.Kd2

{Score: -9.29 68...c1=Q+ 69.Kxc1 Kxf6 70.Kd2 g2 71.Kd3 g1=Q 72.Ke4 Qg2+ 73.Kd4 Qxh3 74.Ke4 Qf5+ 75.Kd4 Qe5+ 76.Kd3 Qd5+

77.Ke3 Ke5 78.Kf2 (Time = 0:55, Depth = 5/11)}

68...g2

{Score: -9.05 69.Kxc2 g1=Q 70.Kd3 Qg3+ 71.Kd4 Qxh3 72.f7 Qg4+ 73.Kd5 Qf5+ 74.Kd4 Kxf7 75.Ke3 (Time = 0:26, Depth =

3/9)}

69.Kxc2

{Score: -9.13 69...g1=Q 70.Kb3 Qg3+ 71.Kb4 Qxh3 72.f7 Qg4+ 73.Kc5 Qf5+ 74.Kd4 Qxf7 75.Ke4 Qe6+ 76.Kf4 Qd5 (Time =

1:51, Depth = 4/10)}

69...g1=Q

{Score: -9.10 70.Kd3 Qg3+ 71.Ke4 Qxh3 72.Kd4 Qg4+ 73.Ke5 Qf5+ 74.Kd4 Kxf6 75.Ke3 (Time = 0:44, Depth = 3/8)}

0-1