I need help, performance wall!

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

nevatre
Posts: 16
Joined: Fri Aug 01, 2008 6:09 pm
Location: UK

Re: I need help, performance wall!

Post by nevatre »

aberent wrote:
nevatre wrote:Adam, this doesn't look correct
"if (alpha > examineBoard.Score)
alpha = examineBoard.Score; "
Did I reverse the operator <>?
yes, i think it should be "if (alpha < examineBoard.Score)".
aberent

Re: I need help, performance wall!

Post by aberent »

CRoberson wrote:
aberent wrote:
CRoberson wrote:Most PC's are running chips based on superscalar architectures. Such machines have performance problems with conditional branching (if statements). So, make your quiesce routine a completely different routine. That will get some speed and make your code much easier to debug.

Second, don't generate the next set of moves just before searching the move you just made.

.......

Notice after making a move you then call search (don't gen moves).

In the Qsearch, same concept about move gen.

Now, prove to me you understand that algorithm. How does the stand pat score get returned? Also, explain the efficiency in when I gen moves in the Qsearch vs how you do it. Why is what I typed better?

When you can answer those questions, you have a chance at improving your program.
Ok so I seperated the Qsearch from regular Alpha Beta. That gave me a small improvement 2%. Better than nothing :)

I think I get it (maybe) you don't call Generate Moves before you enter a deeper depth because you might be at depth 0 in the next move? In that case you just evaluate the position and exit without having to Generate Moves?
No, you don't get it fully. There is much more to it than that. But, lets go with what you have figured out. Now, what is the impact of
not generating moves on the last ply? Think about the branch factor. How many nodes are in the last ply relative to the rest of the tree? Also, how many nodes are in the last ply relative to the previous ply?

Ok so the problem with my code was that I needed to generate valid moves (so I can see what is attacking what) before I evaluated the position. I can see now that this was a huge performance issue.

Once I separated my evaluation from move generation I now have 53% improvement in speed. I can actually play at ply 7 at less than 1 minute per move :) This is huge for me :) Thank you so much.

On top of that my chess engine managed to beet chess titans (the chess program that comes with Windows) at level 10 last night for the first time ever. This was always one of my goals that I could not reach by simply searching to ply 5. I am so happy.

Now back to my education :)

I can see that generating moves just before calling alpha beta, means that I often generated moves just before depth 0, instead of simply returning the score. I also realized that due to the branch factor the unnecessary call at depth 0 is far more damaging to performance than an unnecessary call in an earlier depth.

Please let me know if I got it right.

Thanks allot to everyone that helped :)
aberent

Re: I need help, performance wall!

Post by aberent »

Here is my fixed code, please feel free to comment:

Code: Select all

        private static int SideToMoveScore&#40;int Score, ChessPieceColor color&#41;
        &#123;
            if &#40;color == ChessPieceColor.White&#41;
                return -Score;

            return Score;
        &#125;

        internal static int Quiescence&#40;Board examineBoard, int alpha, int beta, ChessPieceColor movingSide, ref int nodesSearched&#41;
        &#123;
            nodesSearched++;

            //Evaluate Score
            Evaluation.EvaluateBoardScore&#40;examineBoard&#41;;
            //Invert Score to support Negamax
            examineBoard.Score = SideToMoveScore&#40;examineBoard.Score, GetOppositeColor&#40;movingSide&#41;);

            if &#40;examineBoard.Score >= beta&#41;
                return beta;

            if &#40;alpha < examineBoard.Score&#41;
                alpha = examineBoard.Score;

            //We Generate Valid Moves for Board
            PieceValidMoves.GenerateValidMoves&#40;examineBoard&#41;;

            List<Position> positions = EvaluateMoves&#40;movingSide, examineBoard, true&#41;;

            if &#40;positions.Count == 0&#41;
            &#123;
                return examineBoard.Score;
            &#125;

            positions.Sort&#40;Sort&#41;;
        
            foreach &#40;Position move in positions&#41;
            &#123;
                //Make a copy
                Board board = examineBoard.FastCopy&#40;);

                //Move Piece
                Board.MovePiece&#40;board, move.SrcPosition, move.DstPosition&#41;;
               
                int value = -Quiescence&#40;board, -beta, -alpha, GetOppositeColor&#40;movingSide&#41;, ref nodesSearched&#41;;

                if &#40;value >= beta&#41;
                &#123;
                    return beta;
                &#125;
                if &#40;value > alpha&#41;
                    alpha = value;
            &#125;
            return alpha;
        &#125;

        internal static int AlphaBetaFast&#40;Board examineBoard, byte depth, int alpha, int beta, ChessPieceColor movingSide, ref int nodesSearched&#41;
        &#123;
            nodesSearched++;

            if &#40;depth == 0&#41;
            &#123;
                //If last move was a capture
                if &#40;examineBoard.LastMove.TakenPiece.PieceType != ChessPieceType.None&#41;
                &#123;
                    //Perform a Quiessence Search
                    return Quiescence&#40;examineBoard, alpha, beta, movingSide, ref nodesSearched&#41;;
                &#125;

                //Evaluate Score
                Evaluation.EvaluateBoardScore&#40;examineBoard&#41;;
                //Invert Score to support Negamax
                examineBoard.Score = SideToMoveScore&#40;examineBoard.Score, GetOppositeColor&#40;movingSide&#41;);

                return examineBoard.Score;
            &#125;
            //We Generate Valid Moves for Board
            PieceValidMoves.GenerateValidMoves&#40;examineBoard&#41;;

            List<Position> positions = EvaluateMoves&#40;movingSide, examineBoard, false&#41;;

            if &#40;positions.Count == 0&#41;
            &#123;
                //Evaluate Score
                Evaluation.EvaluateBoardScore&#40;examineBoard&#41;;
                //Invert Score to support Negamax
                examineBoard.Score = SideToMoveScore&#40;examineBoard.Score, GetOppositeColor&#40;movingSide&#41;);

                return examineBoard.Score;
            &#125;

            positions.Sort&#40;Sort&#41;;

            depth--;

            foreach &#40;Position move in positions&#41;
            &#123;
                //Make a copy
                Board board = examineBoard.FastCopy&#40;);

                //Move Piece
                Board.MovePiece&#40;board, move.SrcPosition, move.DstPosition&#41;;

                int value = -AlphaBetaFast&#40;board, depth, -beta, -alpha, GetOppositeColor&#40;movingSide&#41;, ref nodesSearched&#41;;
               
                if &#40;value >= beta&#41;
                &#123;
                    return beta;
                &#125;
                if &#40;value > alpha&#41;
                    alpha = value;
            &#125;
            return alpha;
        &#125;
CRoberson
Posts: 2056
Joined: Mon Mar 13, 2006 2:31 am
Location: North Carolina, USA

Re: I need help, performance wall!

Post by CRoberson »

aberent wrote:
Ok so the problem with my code was that I needed to generate valid moves (so I can see what is attacking what) before I evaluated the position. I can see now that this was a huge performance issue.

......

Now back to my education :)

I can see that generating moves just before calling alpha beta, means that I often generated moves just before depth 0, instead of simply returning the score. I also realized that due to the branch factor the unnecessary call at depth 0 is far more damaging to performance than an unnecessary call in an earlier depth.

Please let me know if I got it right.

Thanks allot to everyone that helped :)
Your answer was far more superficial than what I was asking. I'll be more specific. What is the effective branching factor of your
program? Given that, what is the relative size difference between the leaf ply and the ply just before that? Also, what is the size difference
between the leaf ply and the rest of the tree?
Harald
Posts: 318
Joined: Thu Mar 09, 2006 1:07 am

Re: I need help, performance wall!

Post by Harald »

Please answer my questions to yourself. Not to me.
aberent wrote:Here is my fixed code, please feel free to comment:

Code: Select all

        private static int SideToMoveScore&#40;int Score, ChessPieceColor color&#41;
        &#123;
            if &#40;color == ChessPieceColor.White&#41;
                return -Score;

            return Score;
        &#125;
//// You always call this with GetOppositeColor&#40;movingSide&#41;.
//// Change the .White in the function to .Black and call it always with
//// color movingSide.


        internal static int Quiescence&#40;Board examineBoard, int alpha, int beta, ChessPieceColor movingSide, ref int nodesSearched&#41;
        &#123;
            nodesSearched++;

//// Is there anything you will do with 50 move draw or repetition?
//// Here might be one place to react to that.

            //Evaluate Score
            Evaluation.EvaluateBoardScore&#40;examineBoard&#41;;
            //Invert Score to support Negamax
            examineBoard.Score = SideToMoveScore&#40;examineBoard.Score, GetOppositeColor&#40;movingSide&#41;);
//// See above.

            if &#40;examineBoard.Score >= beta&#41;
                return beta;

            if &#40;alpha < examineBoard.Score&#41;
                alpha = examineBoard.Score;

            //We Generate Valid Moves for Board
            PieceValidMoves.GenerateValidMoves&#40;examineBoard&#41;;
//// In qsearch only generate capture &#40;promotion&#41; moves.
//// But generate all moves if movingSide is in check.

            List<Position> positions = EvaluateMoves&#40;movingSide, examineBoard, true&#41;;

            if &#40;positions.Count == 0&#41;
            &#123;
                return examineBoard.Score;
            &#125;
//// Does the Score already include mate or stalemate values?
//// If not you might want to do an movingSide_is_in_check investigation
//// and return special scores&#58; -mateScore = some extreme number
//// and stalemateScore = 0.
//// On the other side if you don't generate all moves here but captures only
//// than you must assume that those situations don't happen and are 
//// hidden inside the stand pat logic.

            positions.Sort&#40;Sort&#41;;
        
            foreach &#40;Position move in positions&#41;
            &#123;
                //Make a copy
                Board board = examineBoard.FastCopy&#40;);

                //Move Piece
                Board.MovePiece&#40;board, move.SrcPosition, move.DstPosition&#41;;
               
                int value = -Quiescence&#40;board, -beta, -alpha, GetOppositeColor&#40;movingSide&#41;, ref nodesSearched&#41;;

                if &#40;value >= beta&#41;
                &#123;
                    return beta;
                &#125;
                if &#40;value > alpha&#41;
                    alpha = value;
            &#125;
            return alpha;
        &#125;

        internal static int AlphaBetaFast&#40;Board examineBoard, byte depth, int alpha, int beta, ChessPieceColor movingSide, ref int nodesSearched&#41;
        &#123;
            nodesSearched++;

//// Is there anything you will do with 50 move draw or repetition?
//// Here might be one place to react to that.

            if &#40;depth == 0&#41;
            &#123;
                //If last move was a capture
//// Don't do this. Read the comment Q below.
                if &#40;examineBoard.LastMove.TakenPiece.PieceType != ChessPieceType.None&#41;
                &#123;
                    //Perform a Quiessence Search
                    return Quiescence&#40;examineBoard, alpha, beta, movingSide, ref nodesSearched&#41;;
                &#125;

                //Evaluate Score
//// Don't do this. Read the comment Q below.
                Evaluation.EvaluateBoardScore&#40;examineBoard&#41;;
                //Invert Score to support Negamax
                examineBoard.Score = SideToMoveScore&#40;examineBoard.Score, GetOppositeColor&#40;movingSide&#41;);

                return examineBoard.Score;
            &#125;
//// Q&#58; Just do this&#58;
////        if &#40;depth == 0&#41;
////        &#123;
////                //Perform a Quiessence Search
////                return Quiescence&#40;examineBoard, alpha, beta, movingSide, ref nodesSearched&#41;;
////        &#125;

            //We Generate Valid Moves for Board
            PieceValidMoves.GenerateValidMoves&#40;examineBoard&#41;;

            List<Position> positions = EvaluateMoves&#40;movingSide, examineBoard, false&#41;;

            if &#40;positions.Count == 0&#41;
            &#123;
                //Evaluate Score
                Evaluation.EvaluateBoardScore&#40;examineBoard&#41;;
                //Invert Score to support Negamax
                examineBoard.Score = SideToMoveScore&#40;examineBoard.Score, GetOppositeColor&#40;movingSide&#41;);

                return examineBoard.Score;
            &#125;
//// Again think about mating and stalemate scores.

            positions.Sort&#40;Sort&#41;;
//// That is ok. Another way to do it is&#58;
//// Just give a quick and dirty value &#40;MVV/LVA + history or + piece_square_value&#41;
//// and pick the next best move from the list in the loop below.
//// Mark moves as used. But sort is easier at first and the pick idea is advanced.

            depth--;
//// If you insist. ;-)
//// But I would prefer to do that in the call only to have the same depth 
//// throughout this whole function. Even after the loop.
//// The only reason to change it here will come when you implement
//// some extensions or reductions later in an advanced engine.

            foreach &#40;Position move in positions&#41;
            &#123;
                //Make a copy
                Board board = examineBoard.FastCopy&#40;);

                //Move Piece
                Board.MovePiece&#40;board, move.SrcPosition, move.DstPosition&#41;;

                int value = -AlphaBetaFast&#40;board, depth, -beta, -alpha, GetOppositeColor&#40;movingSide&#41;, ref nodesSearched&#41;;
//// Here I would use depth-1.
               
                if &#40;value >= beta&#41;
                &#123;
                    return beta;
                &#125;
                if &#40;value > alpha&#41;
                    alpha = value;
            &#125;
            return alpha;
        &#125;
Good mate values would be -32000 + ply (black wins) or +32000 - ply (white wins)
assuming centipawn score.
You know the meaning of ply and the difference in meaning to depth, do you?

When you enter alphaBeta or quiescence it is a good thing to know
if movingSide is in check. Do that in an extra call at the top of the search functions
or somewhere in or after the makeMove() function.

I don't know your board represenntation, move and vove list structure
or move stack. May be there are some issues or unusual solutions also.
Unusual is not bad. It has the chance to be better. Just be aware of it.

Harald
aberent

Re: I need help, performance wall!

Post by aberent »

CRoberson wrote:
aberent wrote:
Ok so the problem with my code was that I needed to generate valid moves (so I can see what is attacking what) before I evaluated the position. I can see now that this was a huge performance issue.

......

Now back to my education :)

I can see that generating moves just before calling alpha beta, means that I often generated moves just before depth 0, instead of simply returning the score. I also realized that due to the branch factor the unnecessary call at depth 0 is far more damaging to performance than an unnecessary call in an earlier depth.

Please let me know if I got it right.

Thanks allot to everyone that helped :)
Your answer was far more superficial than what I was asking. I'll be more specific. What is the effective branching factor of your
program? Given that, what is the relative size difference between the leaf ply and the ply just before that? Also, what is the size difference
between the leaf ply and the rest of the tree?
Currently I don't collect data on how many nodes there are in each ply. It will take me a bit of time to set that up.

Currently from the starting position to ply 7 my software goes through 2,739,203 nodes (That includes QSearch) Is that high?
CRoberson
Posts: 2056
Joined: Mon Mar 13, 2006 2:31 am
Location: North Carolina, USA

Re: I need help, performance wall!

Post by CRoberson »

aberent wrote:
CRoberson wrote:
aberent wrote:
Ok so the problem with my code was that I needed to generate valid moves (so I can see what is attacking what) before I evaluated the position. I can see now that this was a huge performance issue.

......

Now back to my education :)

I can see that generating moves just before calling alpha beta, means that I often generated moves just before depth 0, instead of simply returning the score. I also realized that due to the branch factor the unnecessary call at depth 0 is far more damaging to performance than an unnecessary call in an earlier depth.

Please let me know if I got it right.

Thanks allot to everyone that helped :)
Your answer was far more superficial than what I was asking. I'll be more specific. What is the effective branching factor of your
program? Given that, what is the relative size difference between the leaf ply and the ply just before that? Also, what is the size difference
between the leaf ply and the rest of the tree?
Currently I don't collect data on how many nodes there are in each ply. It will take me a bit of time to set that up.

Currently from the starting position to ply 7 my software goes through 2,739,203 nodes (That includes QSearch) Is that high?
Oh, come on. Forget the code. Do the math.
Jan Brouwer
Posts: 201
Joined: Thu Mar 22, 2007 7:12 pm
Location: Netherlands

Re: I need help, performance wall!

Post by Jan Brouwer »

aberent wrote:
CRoberson wrote:
aberent wrote:
Ok so the problem with my code was that I needed to generate valid moves (so I can see what is attacking what) before I evaluated the position. I can see now that this was a huge performance issue.

......

Now back to my education :)

I can see that generating moves just before calling alpha beta, means that I often generated moves just before depth 0, instead of simply returning the score. I also realized that due to the branch factor the unnecessary call at depth 0 is far more damaging to performance than an unnecessary call in an earlier depth.

Please let me know if I got it right.

Thanks allot to everyone that helped :)
Your answer was far more superficial than what I was asking. I'll be more specific. What is the effective branching factor of your
program? Given that, what is the relative size difference between the leaf ply and the ply just before that? Also, what is the size difference
between the leaf ply and the rest of the tree?
Currently I don't collect data on how many nodes there are in each ply. It will take me a bit of time to set that up.

Currently from the starting position to ply 7 my software goes through 2,739,203 nodes (That includes QSearch) Is that high?
My program needs about 400,000 nodes with pruning disabled (no hash table / null move / late move reduction cuts)
(and 13,425 nodes with all this pruning turned on) so there seems to be room for improvement.

Jan
aberent

Re: I need help, performance wall!

Post by aberent »

What could be causing such a difference in number of searched boards?
Dann Corbit
Posts: 12541
Joined: Wed Mar 08, 2006 8:57 pm
Location: Redmond, WA USA

Re: I need help, performance wall!

Post by Dann Corbit »

aberent wrote:What could be causing such a difference in number of searched boards?
We don't bother searching as deeply when we toss away a queen for no compensation.

For any given chess position, there are about 2-3 good moves. Why bother scouring the other 35? We don't do it when we are playing OTB.

Null move is a demonstration that a move is so bad it's worse than giving up your turn and letting the other guy go. Seems logical that we don't need to scrub the spots off of that one.

LMR is a demonstration that the last eleven times we looked at this move it stunk like a 3 day old baby diaper. No need to look through that basket to find our lunch.

If we spend most of our time looking at the really good moves and only cursory examination of the moves that look really awful, the tree gets much smaller.

Consider:
[d]r1b2rk1/pp2ppbp/2n3p1/q2pP3/Bn5Q/P1N2N2/1PP2PPP/R1B1K2R b KQ -

Here is the multi-pv search for this position:

Code: Select all

      2	00&#58;00	       2.920	186.880	-0.96	Nb4a2
      2	00&#58;00	       2.678	171.392	-0.72	Nb4xc2+
      2	00&#58;00	       1.798	115.072	-0.36	Nb4a6
      2	00&#58;00	       3.807	243.648	-0.27	b7b5
      2	00&#58;00	       2.070	132.480	-0.21	Qa5xa4
   ---------------------------------------------------------------------------
      3	00&#58;00	       6.914	150.636	-1.41	Nb4a2
      3	00&#58;00	       7.261	158.197	-1.38	Nc6xe5
      3	00&#58;00	       6.500	208.000	-0.79	Nb4xc2+
      3	00&#58;00	       4.192	134.144	-0.36	Qa5xa4
      3	00&#58;00	       6.067	194.144	-0.28	Nb4a6
   ---------------------------------------------------------------------------
      4	00&#58;00	      10.267	166.879	-1.54	Nb4a2
      4	00&#58;00	       9.715	157.907	-1.23	Nc6xe5
      4	00&#58;00	       9.429	153.258	-0.75	Nb4xc2+
      4	00&#58;00	       8.599	187.348	-0.31	Qa5xa4
      4	00&#58;00	       8.022	174.777	-0.30	Nb4a6
   ---------------------------------------------------------------------------
      5	00&#58;00	      17.557	163.439	-1.61	Nb4a2 Ra1xa2
      5	00&#58;00	      16.187	150.686	-1.49	Nc6xe5 a3xb4
      5	00&#58;00	      15.107	164.569	-0.86	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO
      5	00&#58;00	      13.428	174.054	-0.38	Qa5xa4 Nc3xa4 Nb4xc2+
      5	00&#58;00	      11.941	154.779	-0.05	Nb4a6 Ba4xc6
   ---------------------------------------------------------------------------
      6	00&#58;00	      48.112	165.881	-1.65	Nb4a2 Ra1xa2 Nc6xe5 Nf3xe5 Bg7xe5 OO
      6	00&#58;00	      45.490	165.771	-1.59	Nc6xe5 a3xb4 Ne5xf3+ g2xf3 Qa5d8 OO
      6	00&#58;00	      42.429	173.789	-0.94	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO Be5xc3 b2xc3 Qa5xc3 Ra1a2
      6	00&#58;00	      38.607	168.227	-0.22	Qa5xa4 Nc3xa4 Nb4xc2+ Ke1e2 Nc2xa1 Na4c3 b7b6 Qh4a4
      6	00&#58;00	      26.111	170.303	-0.05	Nb4a6 Ba4xc6 b7xc6 Qh4xe7 d5d4
   ---------------------------------------------------------------------------
      7	00&#58;01	     237.032	189.625	-1.58	Nc6xe5 a3xb4 Ne5xf3+ g2xf3 Qa5d8 OO Bc8e6 Bc1g5
      7	00&#58;01	     248.515	189.626	-1.43	Nb4a2 Ra1xa2 Nc6xe5 Nf3xe5 Bg7xe5 Qh4b4 Qa5a6
      7	00&#58;01	     233.097	191.105	-1.13	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO d5d4 Nc3e4 Qa5c7 Bc2d3 Bc8d7
      7	00&#58;01	      94.595	177.084	-0.21	Nb4a6 Ba4xc6 b7xc6 OO f7f6 e5xf6 Bg7xf6 Bc1g5
      7	00&#58;01	     124.884	186.144	-0.15	Qa5xa4 Nc3xa4 Nb4xc2+ Ke1e2 Nc2xa1 Na4c3 b7b6 Qh4a4 Bc8d7 Qa4d1 Bd7f5 Qd1xd5
   ---------------------------------------------------------------------------
      8	00&#58;03	     649.388	191.139	-1.75	Nc6xe5 a3xb4 Ne5xf3+ g2xf3 Qa5d8 OO Qd8d6 Bc1g5 f7f6
      8	00&#58;03	     619.870	191.882	-1.56	Nb4a2 Ra1xa2 Nc6xe5 Nf3xe5 Bg7xe5 Qh4b4 Qa5a6 Ba4b3 e7e6 Bc1h6
      8	00&#58;03	     582.372	192.060	-1.01	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO Bc8e6 Bc1e3 Ra8c8 Be3d4 Be5xd4 Qh4xd4 Rc8c4
      8	00&#58;03	     372.897	186.813	-0.16	Nb4a6 Ba4xc6 b7xc6 OO f7f6 e5xf6 Bg7xf6 Bc1g5 Qa5c5 Bg5xf6 e7xf6 Ra1e1
      8	00&#58;03	     308.440	185.680	-0.15	Qa5xa4 Nc3xa4 Nb4xc2+ Ke1e2 Nc2xa1 Na4c3 b7b6 Qh4a4 Bc8d7 Qa4d1 Bd7f5 Qd1xd5
   ---------------------------------------------------------------------------
      9	00&#58;04	     840.599	188.311	-1.75	Nc6xe5 a3xb4 Ne5xf3+ g2xf3 Qa5d8 OO Qd8d6 Bc1g5 f7f6 Bg5e3
      9	00&#58;04	     826.728	187.751	-1.61	Nb4a2 Ra1xa2 Nc6xe5 Nf3xe5 Bg7xe5 Qh4b4 Qa5a6 Ba4b3 e7e6 Bc1h6 Rf8d8 f2f4
      9	00&#58;04	     790.006	187.869	-1.01	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO Bc8e6 Bc1e3 Ra8c8 Be3d4 Be5xd4 Qh4xd4 Rc8c4
      9	00&#58;04	     759.023	188.696	-0.22	Nb4a6 b2b4 Qa5d8 Ba4xc6
      9	00&#58;04	     699.311	188.893	-0.15	Qa5xa4 Nc3xa4 Nb4xc2+ Ke1e2 Nc2xa1 Na4c3 b7b6 Qh4a4 Bc8d7 Qa4d1 Bd7f5 Qd1xd5
   ---------------------------------------------------------------------------
     10	00&#58;09	   1.785.878	189.056	-1.82	Nc6xe5 a3xb4 Ne5xf3+ g2xf3 Qa5d8 OO Bc8f5 Bc1g5 f7f6 Bg5e3 a7a6 Ra1d1
     10	00&#58;09	   1.679.544	189.098	-1.79	Nb4a2 Ra1xa2 Nc6xe5 Nf3xe5 Bg7xe5 Qh4b4 Be5xc3+ b2xc3 Qa5c7 OO a7a5 Qb4b3 Rf8d8 Rf1d1
     10	00&#58;09	   1.081.453	187.283	-1.03	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO Bc8e6 Bc1e3 Ra8c8 Be3d4 Be5xd4 Qh4xd4 Rc8c4
     10	00&#58;09	     998.408	188.316	-0.18	Nb4a6 b2b4 Qa5d8 Ba4xc6 b7xc6 Bc1h6 Na6c7 Bh6xg7 Kg8xg7 OO Nc7e6 Rf1d1
     10	00&#58;09	     930.360	186.181	-0.15	Qa5xa4 Nc3xa4 Nb4xc2+ Ke1e2 Nc2xa1 Na4c3 b7b6 Qh4a4 Bc8d7 Qa4d1 Bd7f5 Qd1xd5
   ---------------------------------------------------------------------------
     11	00&#58;17	   2.567.752	190.011	-1.85	Nb4a2 Ra1xa2 Nc6xe5 Nf3xe5 Bg7xe5 Qh4b4 Be5xc3+ b2xc3 Qa5c7 OO a7a5 Qb4b3 Rf8d8 Rf1d1 Bc8g4
     11	00&#58;17	   3.273.676	195.340	-1.73	Nc6xe5 a3xb4 Ne5xf3+ g2xf3 Qa5a6 Nc3xd5 b7b5 Nd5c7 Qa6c6 Nc7xa8 Qc6xf3 Rh1g1
     11	00&#58;17	   2.436.592	190.842	-1.12	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO Bc8e6 Bc1e3 Ra8c8 Be3d4 Be5xd4 Qh4xd4 Rc8c4 Qd4e5 Qa5b6 Bc2d3 f7f6 Qe5e2 Rc4c6 Bd3b5 Rc6c7
     11	00&#58;17	   2.217.021	191.969	-0.20	Nb4a6 b2b4 Qa5d8 Ba4xc6 b7xc6 Bc1h6 Na6c7 Bh6xg7 Kg8xg7 OO Nc7e6 Rf1d1
     11	00&#58;17	   2.098.056	189.688	 0.00	Qa5xa4 Nc3xa4 Nb4xc2+ Ke1e2 Nc2xa1 Na4c3 b7b6 Qh4a4 Bc8d7 Qa4d1 Bd7f5 Qd1a4 d5d4 Qa4xc6
   ---------------------------------------------------------------------------
     12	00&#58;45	   9.266.869	208.386	-1.66	Nb4a2 Ra1xa2 Nc6xe5 Nf3xe5 d5d4 Ne5c4 Qa5c5 Nc3e4 Qc5xc4 Ba4b3 Qc4c6 OO Bc8f5 Rf1e1 h7h6 f2f3
     12	00&#58;45	   8.626.288	209.092	-1.60	Nc6xe5 a3xb4 Ne5xf3+ g2xf3 Qa5a6 Nc3xd5 b7b5 Nd5c7 Qa6c6 Nc7xb5 Qc6xf3 Rh1g1 Rf8d8 c2c3 Qf3b7 Bc1e3 a7a5 b4xa5 Ra8xa5
     12	00&#58;45	   5.081.152	197.468	-1.18	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO Bc8e6 Qh4xe7 Be5xc3 b2xc3 Qa5xc3 Bc1h6 Rf8e8 Qe7h4 Ra8c8 Bc2a4 Rc8c4
     12	00&#58;45	   4.073.808	197.051	-0.21	Nb4a6 b2b4 Qa5d8 Ba4xc6 b7xc6 Bc1h6 Na6c7 Bh6xg7 Kg8xg7 OO Nc7e6 Rf1d1 h7h6
     12	00&#58;45	   3.801.115	194.461	 0.00	Qa5xa4 Nc3xa4 Nb4xc2+ Ke1e2 Nc2xa1 Na4c3 b7b6 Qh4a4 Bc8d7 Qa4d1 Bd7f5 Qd1a4 d5d4 Qa4xc6
   ---------------------------------------------------------------------------
     13	02&#58;13	  27.096.041	207.634	-1.72	Nb4a2 Ra1xa2 Nc6xe5 Nf3xe5 d5d4 Ne5c4 Qa5c5 OO d4xc3 Bc1e3 Qc5c7 b2b3 Rf8d8 Rf1e1 Bc8f5 Qh4g3 e7e5
     13	02&#58;13	  25.409.082	207.212	-1.62	Nc6xe5 a3xb4 Ne5xf3+ g2xf3 Qa5a6 Nc3xd5 b7b5 Nd5c7 Qa6c6 Nc7xb5 Qc6xf3 Rh1g1 Rf8d8 c2c3 Qf3b7 Bc1e3 a7a5 b4xa5 Ra8xa5 Qh4b4 Ra5a8 Ra1d1 Bc8f5 Rd1xd8+ Ra8xd8 Ba4b3
     13	02&#58;13	  17.600.097	204.763	-1.26	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO Qa5c5 Bc2d3 Bc8d7 Bc1e3 Qc5d6 Ra1e1 f7f6 Nc3d1 Qd6c6
     13	02&#58;13	  12.633.934	207.375	-0.40	Nb4a6 b2b4 Qa5d8 Bc1f4 Na6c7 OO Bc8f5 Ra1d1 a7a6 g2g4 Bf5c8
     13	02&#58;13	  10.537.352	208.334	 0.00	Qa5xa4 Nc3xa4 Nb4xc2+ Ke1e2 Nc2xa1 Na4c3 b7b6 Qh4a4 Bc8d7 Qa4d1 Bd7f5 Qd1a4 Bf5d7 Qa4d1 Bd7f5 Qd1a4 Bf5d7 Qa4d1 Bd7f5 Qd1a4 Bf5d7 Qa4d1 Bd7f5 Qd1a4 Bf5d7 Qa4d1 Bd7f5 Qd1a4 Bf5d7 Qa4d1 Bd7f5
   ---------------------------------------------------------------------------
     14	03&#58;50	  46.032.428	204.451	-1.72	Nb4a2 Ra1xa2 Nc6xe5 Nf3xe5 d5d4 Ne5c4 Qa5c5 OO d4xc3 Bc1e3 Qc5c7 b2b3 Rf8d8 Rf1e1 Bc8f5 Qh4g3 e7e5
     14	03&#58;50	  44.675.747	203.875	-1.62	Nc6xe5 a3xb4 Ne5xf3+ g2xf3 Qa5a6 Nc3xd5 b7b5 Nd5c7 Qa6c6 Nc7xb5 Qc6xf3 Rh1g1 Rf8d8 c2c3 Qf3b7 Bc1e3 a7a5 b4xa5 Ra8xa5 Qh4b4 Ra5a8 Ra1d1 Bc8f5 Rd1xd8+ Ra8xd8 Ba4b3
     14	03&#58;50	  43.143.519	203.284	-1.35	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO f7f6 Bc1e3 Be5xc3 b2xc3 Qa5xc3 Ra1c1 Qc3xa3 Be3c5 Qa3b2 Bc5xe7 Rf8f7 Be7c5 Bc8d7 Bc5d4 Qb2b4
     14	03&#58;50	  38.736.316	206.586	-0.43	Nb4a6 b2b4 Qa5d8 Bc1f4 Na6c7 OO Nc7e6 Rf1d1 Ne6xf4 Qh4xf4 e7e6 Nc3e4 Qd8c7 Ne4f6+ Bg7xf6 Qf4xf6 Bc8d7 Ba4b3 b7b5
     14	03&#58;50	  29.493.880	208.461	 0.00	Qa5xa4 Nc3xa4 Nb4xc2+ Ke1e2 Nc2xa1 Na4c3 b7b6 Qh4a4 Bc8d7 Qa4d1 Bd7f5 Qd1a4 Bf5d7 Qa4d1 Bd7f5 Qd1a4 Bf5d7 Qa4d1 Bd7f5 Qd1a4 Bf5d7 Qa4d1 Bd7f5 Qd1a4 Bf5d7 Qa4d1 Bd7f5 Qd1a4 Bf5d7 Qa4d1 Bd7f5
   ---------------------------------------------------------------------------
     15	09&#58;19	 113.456.587	207.730	-1.77	Nb4a2 Ra1xa2 Nc6xe5 Nf3xe5 Bg7xe5 Qh4b4 Qa5xb4 a3xb4 d5d4 Nc3d5 Bc8e6 Ba4b3 Rf8d8 Ra2a5 Be5d6 Bc1d2 b7b6 Ra5a6 Be6c8 Ra6a1 Bc8b7 f2f3
     15	09&#58;19	  89.050.737	206.980	-1.59	Nc6xe5 a3xb4 Ne5xf3+ g2xf3 Qa5a6 Nc3xd5 b7b5 Nd5c7 Qa6c6 Nc7xb5 Qc6xf3 Rh1g1 Rf8d8 c2c3 a7a6 Nb5d4 Bg7xd4 c3xd4 Bc8e6 Ba4d1 Qf3b7 Bc1d2 Rd8d6 Bd1e2 Ra8c8 Be2d3 Rc8d8
     15	09&#58;19	  82.973.181	205.920	-1.36	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO Bc8e6 Qh4xe7 Rf8e8 Qe7b4 Qa5c7 Bc1e3 Be5xh2+ Kg1h1 Bh2e5 Be3d4 a7a5 Bd4xe5 Qc7xe5 Qb4h4 Ra8c8 Rf1e1 Qe5d6
     15	09&#58;19	  72.607.419	208.430	-0.36	Nb4a6 b2b4 Qa5d8 Bc1f4 Bc8e6 OO Ra8c8 Nc3e2 Na6c7 Ba4xc6 b7xc6 Bf4h6 Qd8d7 Bh6xg7 Kg8xg7 h2h3 Rc8b8 Ra1d1
     15	09&#58;19	  57.070.820	207.554	 0.00	Qa5xa4 Nc3xa4 Nb4xc2+ Ke1e2 Nc2xa1 Na4c3 b7b6 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8
   ---------------------------------------------------------------------------
     16	14&#58;37	 178.522.420	208.499	-1.79	Nb4a2 Ra1xa2 Nc6xe5 Nf3xe5 Bg7xe5 Qh4b4 Qa5xb4 a3xb4 d5d4 Nc3e4 Bc8e6 Ra2a3 Be6d5 f2f3 Rf8c8 OO a7a6 Rf1f2 f7f5 Ne4g5 h7h6
     16	14&#58;37	 160.891.853	209.869	-1.59	Nc6xe5 a3xb4 Ne5xf3+ g2xf3 Qa5a6 Nc3xd5 b7b5 Nd5c7 Qa6c6 Nc7xb5 Qc6xf3 Rh1g1 Rf8d8 c2c3 a7a6 Nb5d4 Bg7xd4 c3xd4 Bc8e6 Ba4d1 Qf3b7 Bc1d2 Rd8d6 Bd1e2 Ra8c8 Be2d3 Rc8d8
     16	14&#58;37	 151.596.771	209.004	-1.28	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO Bc8e6 Qh4xe7 Rf8e8 Qe7b4 Qa5c7 Bc1e3 Be5xh2+ Kg1h1 Bh2e5 Be3d4 a7a5 Bd4xe5 Qc7xe5 Qb4h4 Ra8c8 Rf1e1 Qe5d6 Ra1d1 Qd6b6 Bc2a4
     16	14&#58;37	 135.193.476	210.079	-0.40	Nb4a6 b2b4 Qa5d8 Bc1f4 Bc8e6 OO Ra8c8 Nc3e2 Na6c7 Ba4xc6 b7xc6 Bf4h6 Qd8d7 h2h3 f7f6 Bh6xg7 Kg8xg7 Ne2d4 g6g5 Qh4g3
     16	14&#58;37	 123.029.134	208.755	 0.00	Qa5xa4 Nc3xa4 Nb4xc2+ Ke1e2 Nc2xa1 Na4c3 b7b6 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8
   ---------------------------------------------------------------------------
     17	36&#58;49	 455.465.775	216.942	-1.83	Nc6xe5 Qh4xb4 Ne5xf3+ g2xf3 Bg7xc3+ b2xc3 Qa5c7 OO e7e5 Ra1b1 f7f6 Rf1d1 Rf8d8 Bc1h6 a7a5
     17	36&#58;49	 467.547.514	216.763	-1.75	Nb4a2 Ra1xa2 Nc6xe5 Nf3xe5 Bg7xe5 Qh4b4 Qa5xb4 a3xb4 d5d4 Nc3e4 Bc8e6 Ra2a1 Be6d5 f2f3 Rf8c8 OO a7a6
     17	36&#58;49	 277.532.774	212.698	-1.28	Nb4xc2+ Ba4xc2 Nc6xe5 Nf3xe5 Bg7xe5 OO Bc8e6 Qh4xe7 Rf8e8 Qe7b4 Qa5c7 Bc1e3 Be5xh2+ Kg1h1 Bh2e5 Be3d4 a7a5 Bd4xe5 Qc7xe5 Qb4h4 Ra8c8 Rf1e1 Qe5d6 Ra1d1 Qd6b6 Bc2a4
     17	36&#58;49	 256.768.365	214.168	-0.30	Nb4a6 b2b4 Qa5d8 Bc1f4 Na6c7 OO Nc7e6 Bf4g3 Ne6c7 Ra1d1 h7h6 Qh4f4 g6g5 Qf4e3 Bc8g4 Rf1e1 Bg4xf3 Qe3xf3 e7e6
     17	36&#58;49	 207.680.371	210.429	 0.00	Qa5xa4 Nc3xa4 Nb4xc2+ Ke1e2 Nc2xa1 Na4c3 b7b6 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8 Qh4a4 Bc8d7 Qa4h4 Bd7c8