Patch and tuning by Gary Linscott from an idea of Ryan Taker.
Double tested by Gary:
Wins: 3390 Losses: 2972 Draws: 11323
LOS: 99.999992%
ELO: 8.213465 +- 99%: 6.746506 95%: 5.124415
Win%: 51.181792 +- 99%: 0.969791 95%: 0.736740
And by me:
After 5612 games 1255 1085 3271 +11 ELO
We have a crash with this position:
rkqbnnbr/pppppppp/8/8/8/8/PPPPPPPP/RKQBNNBR w HAha -
What happens is that even if we are castling QUEEN_SIDE,
in this case we have kfrom (B8) < kto (C8) so the loop
that checks for attackers runs forever leading to a crash.
The fix is to check for (kto > kfrom) instead of
Side == KING_SIDE, but this is slower in the normal case of
ortodhox chess, so rewrite generate_castle() to handle the
chess960 case as a template parameter and allow the compiler
to optimize out the comparison in case of normal chess.
Reported by Ray Banks.
Broken by commit a44c5cf4f7 of 3 /12 / 2011 that
was labeled "No functional change" because our 'bench'
test didn't triggered that particular endgame. Indeed
we need to run a specific bench on a set of endgames
position when touching endgame.cpp because normal bench
does not cover endgames properly.
Found by MSVC 2012 code analyzer.
It seems Intel is unable to properly workout templates with 'static'
storage specifier.
Workaround using an anonymous namespace instead.
No functional change.
Currently we exit the loop when
abs(bestValue) >= VALUE_KNOWN_WIN
but there is no logical reason for this. It seems more
natural to re-search again with full open window.
This has practically no impact in most cases, we have a
'no functional change' running 'bench' command.
The trick here is to check for legality only in the
(rare) cases we have pinned pieces or a king move
or an en-passant.
This trick is able to increase the speed of perft
of more then 20%!
No functional change.
Simply reshuffling the code inverting the condition in next_attacker()
yields a miraculous speed up of more than 3% under gcc!
On my laptop a bench run goes from 320Knps to 330Knps
No functional change.
This allows to reduce the scanning for new X-ray attacks
according to the capturing piece type.
It seems to be just a very small speed increase in MSVC 64
bit and gcc 32 bit, I guess cache issues value more than some
instruction less to execute (as usual).
No functional change.
It is very difficult and risky to assure
that a running thread doesn't access a global
variable. This is currently true, but could
change in the future and we don't want to rely
on code that works 'by accident'. The threads
are still running when ThreadPool destructor is
called (after main() returns) and this could
lead to crashes if a thread accesses a global
that has been already freed. The solution is to
use an exit() function and call it while we are
still in main(), ensuring global variables are
still alive at threads termination time.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
When many threds concurrently print you need to serialize
the access to std::cout to avoid output lines are intermixed
with the contents of each thread.
This is not strictly needed at the moment because
only main thread prints out, although some ad-hoc
test could trigger UCI::loop() printing while searching.
Anyhow we want to lift this pretty avoidable constrain
also as a prerequisite for future work.
This patch just introduces the support, next one will enable
the serialization.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Before the search we setup the starting position doing all the
moves (sent by GUI) from start position to the position just
before to start searching.
To do this we use a set of StateInfo records used by each
do_move() call. These records shall be kept valid during all
the search because repetition draw detection uses them to back
track all the earlier positions keys. The problem is that, while
searching, the GUI could send another 'position' command, this
calls set_position() that clears the states! Of course a crash
follows shortly.
Before searching all the relevant parameters are copied in
start_searching() just for this reason: to fully detach data
accessed during the search from the UCI protocol handling.
So the natural solution would be to copy also the setup states.
Unfortunatly this approach does not work because StateInfo
contains a pointer to the previous record, so naively copying and
then freeing the original memory leads to a crash.
That's why we use two std::auto_ptr (one belonging to UCI and another
to Search) to safely transfer ownership of the StateInfo records to
the search, after we have setup the root position.
As a nice side-effect all the possible memory leaks are magically
sorted out for us by std::auto_ptr semantic.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Should be more clear from where the 'magic' numbers
come from.
Also bit of reformat while there.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Instead of just size(). Although code is longer,
should be more immediate to understand when reading.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
To mimics C++11 std::mutex and std::condition_variable,
also rename locks and condition variables to be more
uniform across the classes.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Reduce of one instruction. It seems a tad faster on
the profiler now. Very slightly but anyhow it is a
code semplification.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
This better mimics std::vector::operator[] and
fixes a warning with MSVC 64bit.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Further reduce redundancy in move generation.
Veirifed no speed regression on MSVC, Clang and gcc.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Greatly simplify these very performace critical functions.
Amazingly we don't have any speed regression actually under
MSVC we have the same assembly for generate_moves() !
In generate_direct_checks() 'target' is calculated only
once being a loop invariant.
On Clang there is even a slight speed up.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Are used by Position but do not belong to that class,
there is only one instance of them (that's why were
defined as static), so move to a proper namespace instead.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Check we are the last slave of the split point
before to wake up the master. This should avoid
spurious wakes up.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
We can detect the split point master also from within idle_loop,
so we can call the function without parameters and remove an
overloaded member hack in Thread class.
Note that we don't need to take a lock around curSplitPoint
when entering idle_loop() because if we are the master then
curSplitPoint cannot change under our feet (because is_searching
is set and so we cannot be reallocated), if we are a slave
we enter idle_loop() only upon Thread creation and in that case
is always splitPointsCnt == 0. This is true even in the very rare
case that curSplitPoint != NULL, if we have been already allocated
even before entering idle_loop().
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Directly use the underlying std::map instead and avoid
a useless inheritance.
As a nice side-effect Options global object has now a
default c'tor avoiding possible issues with globals
initializations.
Suggested by Rein Halbersma.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>