Compare commits

...

229 Commits

Author SHA1 Message Date
Marco Costalba e3b23eb818 Stockfish 2.1.1
stockfish bench signature is: 6487630

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-08 08:46:33 +01:00
Marco Costalba d494725400 Spell fix in evaluate.cpp
Spotted by Eelco.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-07 10:08:53 +01:00
Marco Costalba 2535dc1340 Fix reading of book file
Bug is subtle because appears only under MSVC 32 bits in
optimized version, hence was missed before.

Bug is due to the fact that evaluation order of terms of a
sum is undefined by the standard, so in get_int() we have:

return 256 * get_int<n-1>() + bookFile.get();

And if get() is evaluated before get_int() we have a corrupted
key.

The patch rewrites the code in a more natural and predictable way.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-07 09:51:33 +01:00
Marco Costalba 339bf9b7e1 Remove redundant assignment in search()
It is already assigned few lines before.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-05 12:16:26 +01:00
Marco Costalba bb86691a0d Restore development version
No functional change.
2011-05-05 06:35:42 +01:00
Marco Costalba f7fee4c616 Fix a warning in debug mode
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-03 19:29:21 +01:00
Marco Costalba 315c58354e Stockfish 2.1
stockfish bench signature is: 6487630

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-03 12:11:19 +01:00
Marco Costalba 5ef2b8c494 Reintroduce permanent PV entries in TT
We are now ready to release so restore this
improvment before 2.1

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-03 12:11:10 +01:00
Marco Costalba 9c5a53ca45 Update Readme.txt to 32 threads and bsfq on Windows
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-03 12:11:02 +01:00
Marco Costalba b1929960f9 Fix bug in evaluate_passed_pawns()
If blockSq is already on rank 8, blockSq + pawn_push(Us) is on rank 9,
outside of board. It does not make sense to measure king distance to
a field outside the board.

Bug spotted by Fruity:
http://open-chess.org/viewtopic.php?f=5&t=1156&start=10

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-03 12:10:53 +01:00
Marco Costalba 8447248705 Retire "Pawn Structure" UCI option
Almost useless for the user and now is in sync with
the material value that is already weighted.

A small speedup of 0,4% because we avoid an apply_weight()
call in a fast path.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-02 17:50:46 +01:00
Marco Costalba a10487b074 Rename stuff in evaluate.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-02 17:50:37 +01:00
Marco Costalba db31efb8a6 Additional tweaks in evaluate_unstoppable_pawns()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-01 12:15:40 +01:00
Marco Costalba 75acd52415 Remove redundancy in evaluate_unstoppable_pawns()
Spotted by Fruity
http://open-chess.org/viewtopic.php?f=5&t=1156&start=20

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-01 10:31:27 +01:00
Marco Costalba 18c9b5ee86 Small reformat in evaluate_unstoppable_pawns()
Also simplify tracing because evaluate_unstoppable_pawns()
return always zero if both colors have non pawn material.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-01 09:58:45 +01:00
Marco Costalba 33bd67e052 Update polyglot.ini
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-01 07:49:40 +01:00
Marco Costalba 4dc7ba1619 Rename check related functions
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-29 18:56:48 +01:00
Marco Costalba 92d70fb667 Small renaming in thread.cpp
To better self document the code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-29 13:08:35 +01:00
Marco Costalba 0bf475ec55 Rename Option in UCIOPtion
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-29 13:08:27 +01:00
Marco Costalba bb7713c8e9 Limit history range to +-2000
Extensive test series on tweaking history limit and bonus
formula. At the end this was the best.

After 11959 games:

Mod vs Orig 2087 - 1934 - 7938 ELO +4 (+- 3.7) LOS 92%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-28 08:00:34 +01:00
Marco Costalba e656ddcf18 Perft counts leaf nodes not generated moves.
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-27 23:21:46 +01:00
Marco Costalba 1d0159075e Use probe() as name for looking up into an hash table
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-27 07:31:51 +01:00
Marco Costalba 321320b081 Tidy up uci.cpp and siblings
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-26 13:23:47 +01:00
Marco Costalba ca9d40c145 Move OpeningBook and RK where are actually used
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-26 13:23:39 +01:00
Marco Costalba 03f4d1e8d6 Fix a compile error with gcc
It seems gcc does not like an extra semicolon.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-25 22:59:56 +01:00
Marco Costalba 87f2b52ace Move MovePickerExt specializations away from headings
This unclutters a bit the heading part of search.cpp

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-25 13:35:13 +01:00
Marco Costalba 09d01ee9dc Tidy up benchmark.cpp
Node count is different just becuase now we don't log on
"bench.txt" file anymore so that we avoid some calls to
pretty_pv() that calls Position::do_move().

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-25 12:07:30 +01:00
Marco Costalba 05cfb00f26 Large API rename in ThreadsManager
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-25 10:30:39 +01:00
Marco Costalba 339e1b49f6 Don't allocate MAX_THREADS hash tables if not necessary
This prevent crashing on mobile devices with limited RAM,
currently with MAX_THREADS = 32 we would need 44MB that
could be too much for a poor cellphone.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-24 19:23:07 +01:00
Marco Costalba fecefbb99c Move pawn and material tables under Thread class
This change allows to remove some quite a bit of code
and seems the natural thing to do.

Introduced file thread.cpp to move away from search.cpp a lot
of threads related stuff.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-24 17:07:22 +01:00
Marco Costalba c9d7e99de6 Rename MOVES_MAX in MAX_MOVES
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-24 08:54:36 +01:00
Marco Costalba ccd5ccbcdb Retire extensions as UCI option
There is no real need why an user should change these values.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-24 08:52:16 +01:00
Marco Costalba 633c83f648 Document why we use per-thread pawn and material tables
Arisen from a discussion on talkchess.

No fnctional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-24 08:18:39 +01:00
Marco Costalba fe213d30fa Fix some comments in early stop detection
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-23 15:52:51 +01:00
Marco Costalba 8b2adcf99e Retire UseLogFile in search.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-23 15:00:44 +01:00
Marco Costalba 1d368bbbdc Introduce and use SearchLimits
Pack a bit of global variables related to search limits in
a single struct.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-23 13:11:03 +01:00
Marco Costalba bbfe452f85 Use move_is_special() in pawn endgame condition
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-22 12:30:51 +01:00
Marco Costalba 8bf9dc8254 Retire SearchStartTime global
Use a static variable inside current_search_time() instead.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-22 12:30:43 +01:00
Marco Costalba f349143a6b Reduce loops in init_threads() and exit_threads()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-22 12:30:35 +01:00
Marco Costalba 79e50a2fbf Move wake_sleeping_thread() to Thread class
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-20 12:05:07 +01:00
Marco Costalba 2c317d7b28 Correctly implementg selDepth feature
Send to GUI the deepest search depth apart from
qsearch of the PV line.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-20 12:04:59 +01:00
Marco Costalba fdc9f8cbd7 Move sleepLock and sleepCond under Thread
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-20 12:04:52 +01:00
Marco Costalba 2e6839c9a0 Increase risk of blunders at low skill levels
According to Heinz's tests current setup is in fact too
strong for weak players. This seems the best according
to his tests.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-20 12:04:43 +01:00
Marco Costalba 67b0d0b1cc Use only history to score non captures
It seems gain is practically unuseful, so remove.

After 13554 games:
Mod vs Orig 2252 - 2319 - 8983 ELO -1 (+- 3.4)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-17 22:32:12 +01:00
Marco Costalba 068561f86a Small simplification in scale_by_game_phase()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-17 10:31:26 +01:00
Marco Costalba 076b62310e Move ply to SearchStack
Shrink search() signature for better readibility.

We get also a nice 1.3% speed increase.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-17 08:59:54 +01:00
Marco Costalba 5ba85ef441 Remove one indentation level in get_next_move()
Small renaming and fix some comments.

No functional and no speed change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-16 12:57:06 +01:00
Marco Costalba c13b53a514 Code style in tt.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-16 10:49:35 +01:00
Marco Costalba a860576493 Better self-document LMR reduction() formula
Suggested by Onno

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-16 10:49:27 +01:00
Marco Costalba 786564068b Promote OptionsMap to a class
And add a bit of documentation too.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-16 10:49:20 +01:00
Marco Costalba 6056a43419 Fix a stale comment
Spotted by Onno

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-16 10:21:31 +01:00
Marco Costalba f8e767388b Remove src/COPYING file
It is enough the one in the base directory

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-14 07:06:56 +01:00
Marco Costalba b41b590457 Remove "divide by zero" workaround
It is now useless because of the condition at the beginning.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-14 07:04:32 +01:00
Marco Costalba 7f367e6019 Cleanup debug counters
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-13 18:51:56 +01:00
Marco Costalba 54f1c383d3 Move move_is_legal() under Position class
It is a more logical place than in move generation file.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-13 13:18:19 +01:00
Marco Costalba fe8f5b3497 Some more cleanup in endgame.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-13 13:17:21 +01:00
Marco Costalba 6608a16a6a Fix some warnings and a compile error with icc
Unfortunatly icc does not understand that weakerSide and
strongerSide belongs to the base class :-(

So we have define them in the derived class.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-12 08:05:41 +01:00
Marco Costalba b5d5646c84 Move EndgameFunctions to endgame.cpp
And cleanup code while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-11 21:25:24 +01:00
Marco Costalba 08c464c690 Increase MaterialTableSize 8 times
Now that we prefetch in material hash table we
can increase its size and gain something.

Hit rate is now of 98% from 92%

Speedup of 0.8%

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-11 18:15:40 +01:00
Marco Costalba f28ddbb852 Introduce and use NoPawnsSF[] in material.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-11 18:11:16 +01:00
Marco Costalba c88eebc989 Tempeltize material imbalance
Speedup of almost 1%

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-11 12:35:54 +01:00
Marco Costalba 7a84b8ca34 Sync material.h with pawns.h
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-11 12:24:10 +01:00
Marco Costalba 6738b65be9 Prefetch also material tables
Prefetch both pawn and material tables in do_move() and
prefetch always, not only after a pawn move or a capture.

Speed up of 0,7%

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-11 12:24:02 +01:00
Marco Costalba 2f1935078d Assorted code style and comments in pawns.cpp and pawns.h
The only interesting thing is that a backward or isolated
pawn cannot be a candidate passer, so code this condition.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-10 12:16:36 +01:00
Marco Costalba 8e71ee7ec6 Retire mate threat extension
It seems we have a lot of totally useless code !

After 8577 games 1504 - 1451 - 5622 ELO +2 (+- 4.4)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-08 07:48:05 +01:00
Marco Costalba 9bee5f51d8 Fix a compile error in debug mode
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-07 08:09:41 +01:00
Marco Costalba 927f1b0bd3 Assorted code style and comments in search.cpp
Nothing really serious....

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-05 19:00:40 +01:00
Marco Costalba 04108d4541 Added -Wshadow option and fixed resulting warnings
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-04 12:11:01 +01:00
Marco Costalba f7ef48b478 Teach SF to blunder
Add blunder cabability to skill level feature.

The idea is that instead of choosing the best move at the end
of the ID loop, we now do this at a randomly chosen sampling depth
dependent on SkillLevel, so that at low skill levels we sample when
ID loop has reached only a small depth and so we have an higher
probability to pick up a blunder.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-02 10:04:34 +01:00
Joona Kiiski 2e8998eac9 Use prob cut search to prune bad captures
The idea is to try a shallow search with reduced beta
on bad captures so to quickly prune them out in case
are really bad.

After 5529 games 966 - 868 - 3695  ELO +6 (+- 5.4) LOS 91%

Tested also version without upper limitation to 8 plies:

After 8780 games 1537 - 1398 - 5850  ELO +5 (+- 4.3) LOS 93%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-02 08:39:54 +01:00
Marco Costalba 8402b40341 Retire recapture extension also for PvNode
Seems that extension is useless.

After 10105 games
Mod vs Orig 1738 - 1702 - 6665  ELO +1 (+- 4)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-01 22:02:18 +01:00
Marco Costalba 408fdcc93f Use a constant instead of value_mate_in(PLY_MAX)
And also apply the same to value_mated_in(PLY_MAX)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-01 21:46:45 +01:00
Marco Costalba b27e237b04 Retire value_is_mate()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-01 21:46:34 +01:00
Marco Costalba f5b8db7a1e Simplify wait_for_stop_or_ponderhit()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-01 21:46:27 +01:00
Marco Costalba 8d4caebabe Retire update_killers()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-04-01 21:46:12 +01:00
Marco Costalba d173285da5 Fine tune skill level
Rescaled Skill level from 0 to 20. At level 19 is still
comparable with Crafty 20.14, while at low levels strength
increase is now less steep.

Thanks to Joona and Heinz for testing and valuable
comments.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-28 13:09:13 +01:00
Marco Costalba 41fe70d703 Add "Skill level functionality
It is now possible to adjust skill level of Stockfish
from 10 (full strength) to 0.

Skill adjustment is done in such a way that is CPU speed and
time control largely independent, at least at low skills. It
means that given a skill we have same play level on a mobile
phone and on a super OCTAL CPU, at 1' per game or at 180'.

At skill 9 strength is that of an average engine, I have used
Crafty 20.14 to tune and we are more or less there. At skill 0
engine is pretty weak but still shows a realistic play.

When skill is not used we don't have any impact on the regular
code.

Idea to use MultiPV is from Heinz van Saanen, implementation and
formulas by me.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-27 11:50:22 +01:00
Marco Costalba d372f2e39a Fix a compile error with icc
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-26 09:25:26 +01:00
Marco Costalba 4270aec558 Send PV line to GUI only after resolving a fail high
This is how Shredder, Rybka and others do and
avoids user is confused by a fail high (sent to GUI)
followed by a fail low (not sent).

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-24 13:21:54 +01:00
Marco Costalba f427acbd27 Triviality in position.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-24 13:21:47 +01:00
Marco Costalba 87ca13a79a Retire move_ambiguity() altogether
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-23 13:09:04 +01:00
Marco Costalba d52d91064f Simplify move_ambiguity()
And additional small touches in move.cpp

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-22 13:23:41 +01:00
Marco Costalba 920b1abede Do not send ponder move if we don't have it
Has been reported by Justin Blanchard that
this creates problems on some buggy GUI.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-19 16:08:19 +01:00
Marco Costalba 5ea08e79c4 Use intrinsic in pop_1st_bit() under MSVC 64 bits
Around 1% speedup when compiled with MSVC 64

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-17 14:29:09 +01:00
Marco Costalba 635be39acf Additional cleanup in bitbase.cpp
Also better document what code does.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-12 18:22:22 +01:00
Marco Costalba c1c0984452 Move KPKBitbase[] where it belongs
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-12 12:12:41 +01:00
Marco Costalba d653aeca05 Fix a couple of issues in bitbase.cpp
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-12 11:22:02 +01:00
Marco Costalba 09d217bff7 Reintroduce initialization of some bitboards
With off-by-one bug in InFrontBB[] loop fixed.

Also use int instead of File to workaround a bug
in mingw 4.4.0 in first loop that cycles forever.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-10 19:23:39 +01:00
Marco Costalba d9113d127b Rename NonSlidingAttacksBB[] in StepAttacksBB[]
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-10 08:10:26 +01:00
Marco Costalba c2511243b4 Update copyright notes in rkiss.h
New info after a thread on talkchess:

http://www.talkchess.com/forum/viewtopic.php?t=38313

and some emails exchange with Heinz.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-10 08:08:34 +01:00
Marco Costalba d85cf6d9c3 Revert previous patch due to miscompile under gcc
I need to understand what's going on, in the
meantime revert.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-10 08:08:04 +01:00
Marco Costalba a617b03875 Change initialization of some bitboards
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-08 19:44:19 +01:00
Marco Costalba c980163316 Increase MAX_THREADS to 32
No speed regression after 8731 games:
Mod vs Orig 1394 - 1342 - 5995  ELO +2 (+- 4.1)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-08 13:16:33 +01:00
Marco Costalba 76506bd3d1 Introduce and use rot() in rkiss.h
Also fix indentation.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-07 19:04:54 +01:00
Marco Costalba 9069193125 Simplify aspirationDelta update rule
After 7522 games:
Mod vs Orig 1229 - 1148 - 5145  ELO +3 (+- 4.5) LOS 83%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-02 22:01:30 +01:00
Marco Costalba 0c775fae4b Be sure to read options before to call trace_evaluate()
Otherwise in case we change an option with setoption and
then ask for "eval" command the evaluation is not updated.

Spotted by Justin Blanchard.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-03-01 13:32:42 +01:00
Marco Costalba 1f73a9ed63 Fix aspiration corner case
Fix a corner case where we start aspiration window and
suddendly we get a VALUE_KNOWN_WIN / MATE score, this makes
aspiration to blow up in a series of researches loops.

Exit aspiration loop in that case.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-28 20:17:57 +01:00
Marco Costalba af236373cb Remove a FIXME in id_loop()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-28 20:16:22 +01:00
Marco Costalba ce2845d333 Score root move list during first iteration
Use first iteration to get a proper startup score
and possibly detect an easy move.

After 5180 games:
Mod vs Orig 847 - 823 - 3510  ELO +1 (+- 5.5)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-28 07:51:28 +01:00
Marco Costalba 204efb109b Remove an useless condition in equal SEE pruning
Because we are never in check there and evaluation cannot
return a mated value the condition is useless.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-27 09:45:54 +01:00
Marco Costalba 91a2766308 Prune moves with equal SEE in qsearch
After 5166 games:
Mod vs Orig 890 - 762 - 3514  ELO +8 (+- 5.5) LOS 96%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-27 09:19:59 +01:00
Marco Costalba bd33766da0 Add evaluation tracing code
This patch is based on Justin Blanchard's original
work and allows to breakdown evaluation in its sub terms and
show to the user.

Tracing code has zero speed impact when not used.

Note that tracing code is not thread-safe, but this
should not be a problem given the typical usage scenario.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-26 14:26:27 +01:00
Marco Costalba dd718d92a7 Correctly round evaluation to grain size
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-23 23:52:51 +01:00
Marco Costalba 23cbb221b2 Depth dependant singular extension margin
After 7965 games:
Mod vs Orig 1324 - 1249 - 5392  ELO +3 (+- 4.4) LOS 81%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-23 23:51:20 +01:00
Marco Costalba 0fcda095df Move all enum types definitions to types.h
Cleanup headers while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-23 21:52:55 +01:00
Marco Costalba 95212222c7 Retire color.h
Move contents to piece.h and square.h

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-23 18:42:45 +01:00
Marco Costalba 2ff2b59727 Rename piece_of_color_and_type() to make_piece()
To be aligned with make_square()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-23 18:42:35 +01:00
Marco Costalba 5b2ac7590c Retire piece_type_from_char()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-23 18:42:27 +01:00
Marco Costalba 2511386155 Triviality in main.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-23 18:41:58 +01:00
Marco Costalba deb212cb05 Retire enum SquareDelta
Use Square instead. At the end is the same because we were
anyway foreseen operators on mixed terms (Square, SquareDelta).

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-23 18:41:35 +01:00
Marco Costalba aa40d0a917 Small simplifications in square.h
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-23 18:41:27 +01:00
Marco Costalba b3108547de Introduce and use speed_to_uci()
And retire nps()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-19 13:31:35 +01:00
Marco Costalba 706b44a966 Rename SplitPoint parentSstack
Now that we don't have anymore a search stack array in
SplitPoint we can rename this data member to somthing more
usual.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-18 17:04:55 +01:00
Marco Costalba 823a5918e7 Retire SearchStack sstack[] from SplitPoint
Use a local variable instead. To make it work we need to
correctly init next ply search stack at the beginning of the
search because now that ss is allocated on the stack instead
of on the global storage it contains garbage.

As a side effect we can peform a fast search stack
init in id_loop().

With this patch size of SplitPoint goes from 71944 to 136 bytes,
and consequently size of Thread goes from 575568 to 1104 bytes.

Finally the size of ThreadsManager that contains all the thread
info goes from 9209248 to just 17824 bytes !!

No functional change also in faked split.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-18 16:54:49 +01:00
Marco Costalba 932ae761c6 Sync Root new depth to what we do in search()
This allow us to restore the old depth 12 benchmark
and fixes one and for all the depth mess.

Test confirms no regression:
After 5658 games 892 - 924 - 3842  ELO -1 (+- 5.2)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-18 11:24:42 +01:00
Marco Costalba 29fa6f3c5f Unify best move update logic
Try to rewrite the Root case using as most as common
code as possible.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-17 09:51:22 +01:00
Marco Costalba 5815718177 Do not special case reductions for MultiPV case
Note that this introduces an asymmetry in which best move
is searched deeper then others also in MultiPV, but this is
not an error per se.

No functional change when MultiPV = 1

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-17 09:08:27 +01:00
Marco Costalba 6a19f5832a Avoid permanent PV entries in TT
This patch removes a condition that allows a PV entry to remain
in TT across games for an unlimited time.

Although this produces a nice ELO boost in the long term it
is an artifact that affects tests results bewteen version
with and without this feature.

So remove now and readd before to release because it actually
seems a strong feature.

As example a verification tournament against SF 2.0.1 starting around
+10 ELO after 4K games sligltly climbed to +21 ELO after 14K games !!!

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-16 08:57:35 +01:00
Marco Costalba 4ead60e2a7 Write the LogFile only at the end of an iteration
Skip writing fail high/low sequences. Note that we don't need
fail high/low markers anymore in pretty_pv().

No functional change but some do/undo move sequences.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-13 11:45:49 +01:00
Marco Costalba 2786aed195 Spell checking fixes in search.cpp
Reported by Eelco on open-chess.org

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-12 19:06:07 +01:00
Marco Costalba 876ceb1feb Rename iteration in depth in id_loop()
And retire the redundant one.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-12 18:53:02 +01:00
Marco Costalba 29076043e0 Start to count iterations from 1
First search should be done at iteration = 1, not 2. So offset
the variable by one.

As a nice side effect now search correctly stops at PLY_MAX
included, not after searching (PLY_MAX - 1) as before.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-12 18:17:37 +01:00
Marco Costalba aa84731fb9 Fix wrong reported depth
Interestingly this patch will make people complain search depth
is reduced against 2.0.1 ;-) but actually it is only an artifact.

Spotted by Joona.

No functional change apart from a different do / undo move
sequence due to teh fact that we don't call pv_info_to_uci()
anymore before entering id loop.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-12 17:58:25 +01:00
Marco Costalba c006435bb4 Move sending of PV line to id_loop()
No functional change apart form move reordering because
pv_info_to_uci() performs a do / undo_move sequence.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-11 06:28:55 +01:00
Marco Costalba 141410f177 Maximum aspiration delta of 24
After 9080 games
1430 - 1342 - 6308  ELO +3 (+- 2.9)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-10 21:09:20 +01:00
Joona Kiiski 3abff79df3 Maximum aspiration delta of 64
After 9242 games
Mod vs Orig: 1483 - 1373 - 6386  ELO +4 (+- 2.9)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-09 23:52:24 +01:00
Marco Costalba 62c707e1d5 Simplify latest patches
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-06 12:41:47 +01:00
Joona Kiiski 26e7673c18 Retire some conditions from ok_to_use_TT_pv
After 4844 games 768 - 747 - 3329  ELO +2

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-02-05 11:06:08 +01:00
Marco Costalba b366c7dc38 Use TT for pruning also in PV nodes
Biggest advantage is be able to analize positions
without "loss of memory" when goind back/forth in
a position.

Patch has proven to fix analysys problems and is even
worths some elo points.

After 5811 games Mod- Orig:
1037 - 902 - 3872 +8 ELO  (+- 3.6) LOS 97%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-31 13:07:26 +01:00
Marco Costalba 2f6788104f Silence silly MSVC warning c4146
Warning C4146: unary minus operator applied to
unsigned type, result still unsigned.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-29 14:10:05 +01:00
Marco Costalba 69726f4df3 Remove defined(IS_64BIT) in init_sliding_attacks()
No functional change bith in 32 and 64 bits.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-29 14:06:29 +01:00
Marco Costalba f3e0b32def Do not use <algorithm> in to_fen()
Seems there are some problems on HP-UX compiler.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-29 13:28:00 +01:00
Marco Costalba 188700d7f1 Retire obsolete reentrancy check in pos.print()
We dont' call MovePicker from print() anymore, so that
reentrancy check in now not needed.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-29 12:17:47 +01:00
Marco Costalba 9ba7f701ea Retire singleEvasion
This let us get rid of number_of_evasions()

After 5487 games
Mod- Orig: 851 - 852 - 3784 +0 ELO  (+- 3.7)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-27 07:55:04 +01:00
Marco Costalba afae86bfb4 Add a MovePicker c'tor specialized for qsearch
This simple patch shows a speed increase of
more then 2% !

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-23 23:13:14 +01:00
Marco Costalba f352008958 Introduce and use qsearch_scoring()
Move qsearch scoring functionality out of RootMoveList
initialization. Will be needed by future patches.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-23 10:54:16 +01:00
Marco Costalba 6849f0800e Retire InitialDepth
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-23 10:52:51 +01:00
Marco Costalba 5194b2bb06 Pass TT move instead of Rml[0].pv[0] to MovePicker
This is used for secondary scoring so it does not
changes the fact that Rml[0].pv[0] is always tried
as first anyhow.

It happens this is even a no functional change patch
becuase we reinsert PV in TT after a search so that
TT move is actually Rml[0].pv[0].

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-23 09:11:31 +01:00
Joona Kiiski a8f457d425 Different searchedMoves system
After 8751 games on russian cluster
Mod- Orig: 1426 - 1323 - 6002  ELO +4 (+- 2.9) LOS 86%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-23 08:36:47 +01:00
Marco Costalba 79b1a7417f Remove special Root cases
So to better spot where the differences really
count. Also add some more additional cleanup.

Harmless functional change and no regression.

After 5780 games
Mod- Orig: 931 - 955 - 3894 ELO -1 (+- 3.6)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-23 08:36:12 +01:00
Marco Costalba b67de36671 Retire init_ss_array()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-20 19:34:25 +01:00
Marco Costalba 59c85346d2 Small cleanup in init_sliding_attacks()
No functional change both in 32 and 64 bits.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-19 19:50:44 +01:00
Marco Costalba e8f885145b Numbers formatting in bitboard.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-19 19:50:32 +01:00
Marco Costalba 324ca87aff Use opposite_color_squares() instead of same_color_squares()
It is almost alwasy the requested test and is a bit faster too.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-19 13:50:51 +01:00
Marco Costalba 4f3fe89fb6 Retire RelativeRankBB[]
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-19 13:33:38 +01:00
Marco Costalba 45acec1865 Retire some unused functions in bitboard.h
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-19 13:33:26 +01:00
Marco Costalba a38b14bd33 Fix some warnings under icc
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-18 18:53:19 +01:00
Marco Costalba d91d6da3c4 Sort root moves moves in MovePickerExt
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-18 18:52:52 +01:00
Marco Costalba 5bad5fc0a7 Fix a (bestValue == -VALUE_INFINITE) assert
In case of a Root node we can leave with bestValue set
to -VALUE_INFINITE if search is stopped by the GUI and
stopReques flag is raised.

This patch fixes the issue.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-18 13:30:13 +01:00
Marco Costalba 6119e4ea37 Additional cleanup in id_loop()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-18 13:30:13 +01:00
Marco Costalba 5555b60b38 Use a global RootMoveList object instead of a pointer
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-18 13:30:12 +01:00
Marco Costalba 846087e4fb Move globals to id_loop()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-18 13:30:11 +01:00
Marco Costalba 2e2f1064ba Introduce and use MovePickerExt
A bit of template magic to restore a proper and readable moves
'while' loop that now is again 'similar' to the one that used
to be in search().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-18 13:30:11 +01:00
Marco Costalba 392c7f2ab6 Unify root_search() step 3
Retire root_search()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-18 13:30:10 +01:00
Marco Costalba 6b8026806c Unify root_search() step 2
Enable the change: now we use search() instead of root_search()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-18 13:29:57 +01:00
Marco Costalba 299bda9ea3 Unify root_search() step 1
Teach search() to behave as a root node if requested.
Just added code, but still no functional change.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-18 13:28:34 +01:00
Joona Kiiski 998845763a Fix very theoretical History corner case
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-17 20:22:10 +01:00
Marco Costalba 842efefcad Sync root_search() with search()
This will let unification easier.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-16 13:45:34 +01:00
Marco Costalba c17a127c42 Move fail loops out of root_search() to id_loop()
And sync root_search() with search()

After 9384 games Mod - Orig:
1532 - 1433 - 6419  ELO +3 (+- 2.8)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-16 13:45:23 +01:00
Marco Costalba e06c99cad0 Last touches in history.h
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-15 18:03:43 +01:00
Marco Costalba 04001f776e Fix a warning with __popcnt64() intrinsics
Returns an int64_t while we want a simple int.

This occurs only when compiling with MSVC on a 64 bit platform.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-15 12:05:31 +01:00
Marco Costalba 877b468e3e Partially restore HistoryMax
Should be not useful but better safe than sorry.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-15 11:31:14 +01:00
Marco Costalba 13a42284b6 Move Min() and Max() macros to types.h
As usual a bit of cleanup while there...

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-15 11:00:00 +01:00
Marco Costalba 3bb1ab34e4 Clarify we want Score and Value to be integers
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-15 10:37:36 +01:00
Marco Costalba 7faeab0878 Retire history.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-15 10:02:45 +01:00
Marco Costalba 2052407090 Retire HistoryMax
Infact we don't use it anymore already.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-15 09:30:39 +01:00
Joona Kiiski d6fdd4f6d9 Set HistoryMax infinitely high
Respin this old idea. Earlier we tried only
with < 1000 games and result was inconclusive.

After 5845 games
Mod vs Orig: 935 - 936 - 3974 ELO (+-3.6)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-14 19:13:22 +01:00
Marco Costalba b85bcc039c Simplify from_fen()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-14 12:56:31 +01:00
Marco Costalba 62cd133b3a Initialize killers at ss+2 also in root_search()
After 4955 games:
Mod - Orig: 786 - 768 - 3401 +1 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-14 07:27:06 +01:00
Marco Costalba 6442981cb6 Fix an hang on 32 bits while allocating big TT table
If size_t is defined as a 32 bit quanitity then we have an
overflow in the left term of the while condition if mbSize
is bigger then 2048.

For instance if mbSize is 2049 then when newSize will reach
0x80000000 (2048MB) comparison is still true, 'while' loops
again and we have an overflow in the expression (2*newSize)
so that result is 0 and at that point 'while' keeps looping
forever hanging the application.

This patch fixes the bug and also makes operator new do not
throw an exception upon failure but return a NULL pointer
instead.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-13 22:03:18 +01:00
Marco Costalba d4b92ae9a0 Set unbuffered I/O also for C standard library
In input_available() we use function select(), so
we have to set as unbuffered also C library I/O
functions otherwise we can miss some input.

For instance in case GUI sends "go infinite\nstop\n" we
parse the "go infinite" but then input_available() under Linux
is unable to detect that we still have "stop" to be processed.

This is because "select" uses file descriptors instead of file
pointers. So it cannot know about the buffer associated to a file
pointer.

This patch, by BB+, should fix the problem.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-11 18:58:56 +01:00
Marco Costalba 611a29f767 Big book.cpp cleanup
Better document PolyGlot formats and greatly
reduce line count.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-10 19:55:02 +01:00
Marco Costalba d84ffc0cfa Compile fix in types.h
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-10 12:54:08 +01:00
Marco Costalba caa02b0e43 Small cleanup in execute_uci_command()
With a little fall out in siblings functions...

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-09 16:24:30 +01:00
Marco Costalba a8741bd59f Simplify set_option()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-09 15:15:52 +01:00
Joona Kiiski 06c14d0a37 TTEntry simplification
Now that move is fitted in 16 bits we can simplify TTEntry.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-09 13:20:04 +01:00
Marco Costalba 15153a1de7 Don't copy Position in pretty_pv()
Also let do_setup_move() don't reuse same StateInfo so that
we can remove the check about different StateInfo objects
before memcpy() in do_move.

Functional change due to harmless additionals
do_move() / undo_move() steps.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-09 12:55:22 +01:00
Marco Costalba c89762288b Merge line_to_san() into pretty_pv()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-08 18:31:14 +01:00
Marco Costalba b4acf83704 Ressurect move.cpp
Actually it is san.cpp renamed. Because now has the move
conversions functions and doesn't have any more the bulky
move_from_san(), it is better to call it move.cpp

Remove san.h while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-08 17:52:02 +01:00
Joona Kiiski ee0afea1e5 Fix build failure under Linux
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-08 16:26:55 +01:00
Marco Costalba c82906a2c3 Retire move_from_san()
It is unused and it is big.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-08 16:23:46 +01:00
Marco Costalba 6b49d509a1 Greatly simplify move_from_uci()
Use a reverse logic: among the list of generated legal moves
transformed in UCI coordinate notation find the one that
matches the given string.

It is a bit slower, but here is not performance critical and
is much more simplified then before.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-08 16:12:42 +01:00
Marco Costalba 82d5386435 Move uci move parsing under san.cpp
This partially reverts 1e7aaed8bc keeping the conversion
functions from/to move to uci string in the same file.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-08 15:53:49 +01:00
Joona Kiiski f4b4d4901c Change Emergency time maximum from 60000 to 30000
I got report from Werner that Shredder Gui has problems with
UCI values which maximum value is greater than 30000.

Of course it's stupid to change engine to fix a GUI's bug,
but on the other hand 30000 ms as maximum value is clearly enough,
so why not to be merciful

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-08 14:27:11 +01:00
Joona Kiiski 8d86e95e37 Fix Makefile's GPL-notice to be similar to other files
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-08 14:26:59 +01:00
Marco Costalba 0f81f97bb6 Improve I/O responsivness
Added checking of (stdin->_cnt > 0) from Greko.

This seems to greatly improve responsivness when running
under console. Now while running a 'stockfish bench', any key
press immediately is detected by SF while before there was a
delay of some fraction of a second.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-08 14:17:41 +01:00
Marco Costalba d9b96f0e49 Fix reading a book under-promotion move
This is an old Glaurung bug that prevented a Polyglot
book move to be read correctly in case of underpromotion.

This patch fixes the bug restoring support for both
queen and underpromotions.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-08 13:23:25 +01:00
Marco Costalba 4fa0395eb8 Triviality in data_available()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-08 12:53:41 +01:00
Marco Costalba b3545737fa Force inlining of move generation functions
MSVC (and possibly other compilers) does not inline
as requested, so force it to do so.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-07 16:57:15 +01:00
Marco Costalba 44fbbeafc9 Small tweak to generate_castle_moves()
Move the castling condition test out of the
function. This avoids a function call most of
the times.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-07 16:57:14 +01:00
Marco Costalba 57b3ca916f Unify move generation
Functional change due only to moves reorder. Anyhow after
5242 games at 15"+0.1 TC verified we have no regression.

Mod vs Orig 994 - 958 - 3290 +2 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-07 16:56:42 +01:00
Marco Costalba 12f4bbc8f2 Templetize move generation API
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-07 14:46:29 +01:00
Marco Costalba 1e7aaed8bc Retire move.cpp
Move its functions where they belong.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-07 13:40:13 +01:00
Marco Costalba a46b53e1c2 Use 16 bits to store a move instead of 17
Shrink of 1 bit so to fit a move in an uint_16 and
possibly a MoveStack in an uint_32.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-07 12:13:05 +01:00
Marco Costalba dcbc8a7e75 Use a 32 bit bitwise 'and' in SimpleHash lookup
A bit faster on 32 bits machines, more similar to
TranspositionTable::first_entry() and same result.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-07 11:25:13 +01:00
Marco Costalba 0ddf84870a Introduce SimpleHash class
And use it for pawns and material infos.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-07 11:20:49 +01:00
Marco Costalba 803c8e0be3 Small tidy up of inttypes for Windows
There was a strange "int16" type and "int64_t"
was defined twice.

Spotted by Joona.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-06 23:28:06 +01:00
Joona Kiiski fca74b1882 Simplify 50 move rule condition
We never reach a position where rule50 > 100.
When rule50 == 100, it's either draw or mate and
there is no way search could go deeper.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-06 14:46:49 +01:00
Joona Kiiski b08ba446f6 Clean up position setup code
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-06 13:54:03 +01:00
Joona Kiiski 1a20d72701 Parse halfmove clock and fullmove number from FEN
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-06 13:44:11 +01:00
Joona Kiiski 916c0cbfbc Minimal restructuring of value.h
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-06 13:36:02 +01:00
Marco Costalba 2e46db4369 Do not make any assumption on the move in move_is_legal()
We must be able to filter out also moves where move_is_ok()
is false.

And actually we are. Tested on all the default position injecting
a number from -1000000 to 1000000 casted to a Move.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-06 13:33:40 +01:00
Marco Costalba 5fc8f86a4f Change move_is_ok() and square_is_ok() in something useful
As is defined now is always true, tested with:

  for (long i=-1000000; i < 1000000; i++)
      if (!move_is_ok(Move(i)))
          exit(0);

Reason is that move_from() and move_to() already truncate the
input value to something in the range [0, 63] that is always
a possible square.

So change definition to something useful.

The same applies also to square_is_ok()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-06 10:55:48 +01:00
Marco Costalba c14dae1fa2 Improve update_killers() signature
Will be used by future patches and is cleaner.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-06 09:09:53 +01:00
Joona Kiiski d4ded09e17 Fix variable naming in prototypes at uci.cpp
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-05 23:41:08 +01:00
Joona Kiiski 631fa6a100 Remove a false comment
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-05 23:38:33 +01:00
Marco Costalba dadf6a6fe9 Set moveCount base to 1 as in search()
Now first move has moveCount == 1 also in root_search()

Also added small readibility touches.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-05 23:19:12 +01:00
Marco Costalba 6a5dc14251 Use killers also in root_search()
After 4238 games
Mod-Orig 800 - 686 - 2752 +9 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-05 23:17:28 +01:00
Marco Costalba 55b16593a4 Perft should return an int64_t not an int
Found by Louis Zulli with his super fast
hardware: 65M nodes/sec at perft !

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-05 21:13:21 +01:00
Marco Costalba 7614501362 Fix POPCNT support for Intel compiler under Windows
Reported by Martin Wyngaarden that also confirmed
this patch to work.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-05 21:12:19 +01:00
Marco Costalba 22ca7a601f Esplicitly inline generate_piece_moves() & friends
Should be already inlined by the compiler when
optimizing but better safe than sorry ;-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-05 09:59:20 +01:00
Marco Costalba 97212bafc9 Use 'moveCount' name also in RootSearch
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-04 11:35:54 +01:00
Marco Costalba 07fcc8076d Use -O3 instead of -fast for Linux icc
Reported by Heinz and confirmed by Joona to increase
the speed of 6% !

No change for icc on OSX

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-04 11:12:43 +01:00
Marco Costalba be5b32bb9c Another round of bitboard.cpp cleanups
Also renamed StepAttackBB[] in NonSlidingAttacksBB[]

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-04 11:12:31 +01:00
Marco Costalba 9da1f45b1d Restore development version
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-04 09:55:42 +01:00
Marco Costalba 096351d1f5 Stockfish 2.0.1
Always same siganture: 7224363

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-04 08:24:05 +01:00
Marco Costalba 6b96e6f33d Update Readme and polyglot files
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-03 23:55:12 +01:00
Marco Costalba f200f3ccd2 Another attempt at fixing Chess960
Keep the isChess960 flag inside Position so that is
copied with the Position, but esplicitly highlight the
fact that a FEN string has not enough information to detect
Chess960 in general case. To do this add a boolean argument
isChess960 to from_fen() function so to self document this
shortcoming of FEN notation.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-03 22:50:38 +01:00
Marco Costalba 2bb555025f Revert Chess960 fix
Will be substituted by a better next patch.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-03 22:48:08 +01:00
Tord Romstad d5f2e32b5c Reintroduce the old "trapped bishop in the corner" evaluation term
for Chess960 games.

After 1918 games at 30"
Mod - Orig: 1052-866 (+532,-346,=1040), Elo +33.8
2011-01-03 22:32:57 +01:00
Joona Kiiski 83d8d54216 Use simple macro to enable operators
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-03 22:27:17 +01:00
Marco Costalba 078354060e Workaround broken function-style cast support in HP-UX
It seems HP's ANSI C++ doesn't understand very well
standard function-style cast.

Reported by Richard Lloyd.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-03 11:36:32 +01:00
Marco Costalba 5973e09854 Readd SRWLOCK and Condition Variables under Windows
And set them as default.

Introduce compile switch OLD_LOCKS to allow to fallback on
compatible locks supported by Windows XP and older versions.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-03 10:49:33 +01:00
Marco Costalba 22ede4442c Fix Chess960 regression
Introduced by me in before 1.9 and found by Tord that says:

The 'isChess960' slot in the 'Position' class is currently
set depending on the initial files of the rooks, and not on the value
of the UCI_Chess960 parameter. This is incorrect, as there are lots of
Chess960 positions where the rooks start on the usual files. As a
consequence (unless I am missing something), Stockfish will occasionally
output castling moves as e1g1/e1c1 rather than the correct e1h1/e1a1 format
in Chess960 games. It is possible that some or even most GUIs are robust
enough to accept both notations, but I wouldn't bet on it. And in any case,
Stockfish's behavior clearly violates the protocol.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-03 00:41:39 +01:00
Marco Costalba deee18c758 Another (final?) attempt at squares_delta()
This time I have removed the function alltogether !

Sorry to work above a patch of UncombedCoconut (Justin Blanchard)
but I couldn't resist ;-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-02 11:52:51 +01:00
Marco Costalba 0693ff178e Fix old Glaurung bug related to search logging
When we log best and ponder move to a file before to
return from think we change the position. If position is
then not resended by GUI, as for manual user input we got
an error:

justinb@malibu:~$ stockfish
Stockfish 2.0 JA 64bit by Tord Romstad, Marco Costalba, Joona Kiiski
setoption name Use Search Log value true
go depth 1
info depth 1
info depth 1 seldepth 1 multipv 1 score cp 72 time 59 nodes 20 nps 338 pv g1f3
info depth 2
info depth 2 seldepth 2 multipv 1 score cp 12 time 59 nodes 44 nps 745 pv g1f3 g8f6
info nodes 84 nps 1423 time 59
bestmove g1f3 ponder g8f6
go depth 1
info depth 1 score mate 0
info nodes 87 nps 0 time 0
bestmove (none) ponder (none)

Bug spotted and fixed by UncombedCoconut.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-02 11:22:41 +01:00
Marco Costalba 57c51dd1ef Simplify squares_delta()
And rename in ray_direction()

Patch from UncombedCoconut.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-02 10:59:08 +01:00
Marco Costalba f902ddaa89 Fix a crash on multi-pv
Bug reported by Tobias Haspel and fixed by Joona.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-01 23:10:37 +01:00
Marco Costalba 24485df3df Restore development version
And set "Use Sleeping Threads" to true because it keeps
much more responsive and cool my QUAD during tests :-)

It will be reverted back before to release that's the
reason to bundle it here.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-01 16:19:08 +01:00
54 changed files with 4380 additions and 6558 deletions
+12 -9
View File
@@ -8,8 +8,8 @@ Chess Partner, or Fritz) in order to be used comfortably. Read the
documentation for your GUI of choice for information about how to use
Stockfish with your GUI.
This version of Stockfish supports up to 8 CPUs, but has not been
tested thoroughly with more than 2. The program tries to detect the
This version of Stockfish supports up to 32 CPUs, but has not been
tested thoroughly with more than 4. The program tries to detect the
number of CPUs on your computer and set the number of search threads
accordingly, but please be aware that the detection is not always
correct. It is therefore recommended to inspect the value of the
@@ -41,10 +41,10 @@ This distribution of Stockfish consists of the following files:
3. Opening books
----------------
This version of Stockfish has experimental support for PolyGlot opening
books. For information about how to create such books, consult the
PolyGlot documentation. The book file can be selected by setting the
UCI parameter "Book File".
This version of Stockfish has support for PolyGlot opening books.
For information about how to create such books, consult the PolyGlot
documentation. The book file can be selected by setting the UCI
parameter "Book File".
4. Compiling it yourself
@@ -60,9 +60,12 @@ Stockfish has POPCNT instruction runtime detection and support. This can
give an extra speed on Core i7 or similar systems. To enable this feature
compile with 'make icc-profile-popcnt'
On 64 bit Unix-like systems the 'bsfq' assembly instruction will be used
for bit counting. Detection is automatic at compile time, but in case you
experience compile problems you can comment out #define USE_BSFQ line in types.h
On 64 bit systems the 'bsfq' assembly instruction will be used for bit
counting. Detection is automatic at compile time, but in case you experience
compile problems you can comment out #define USE_BSFQ line in types.h
In general is recommended to run 'make help' to see a list of make targets
with corresponding descriptions.
5. Terms of use
+6 -15
View File
@@ -24,25 +24,16 @@ Use Search Log = false
Search Log Filename = SearchLog.txt
Mobility (Middle Game) = 100
Mobility (Endgame) = 100
Pawn Structure (Middle Game) = 100
Pawn Structure (Endgame) = 100
Passed Pawns (Middle Game) = 100
Passed Pawns (Endgame) = 100
Space = 100
Aggressiveness = 100
Cowardice = 100
Check Extension (PV nodes) = 2
Check Extension (non-PV nodes) = 1
Single Reply Extension (PV nodes) = 2
Single Reply Extension (non-PV nodes) = 2
Mate Threat Extension (PV nodes) = 0
Mate Threat Extension (non-PV nodes) = 0
Pawn Push to 7th Extension (PV nodes) = 1
Pawn Push to 7th Extension (non-PV nodes) = 1
Passed Pawn Extension (PV nodes) = 1
Passed Pawn Extension (non-PV nodes) = 0
Pawn Endgame Extension (PV nodes) = 2
Pawn Endgame Extension (non-PV nodes) = 2
Randomness = 0
Minimum Split Depth = 4
Maximum Number of Threads per Split Point = 5
Use Sleeping Threads = false
Skill Level = 20
Emergency Move Horizon = 40
Emergency Base Time = 200
Emergency Move Time = 70
Minimum Thinking Time = 20
-674
View File
@@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+8 -11
View File
@@ -2,8 +2,6 @@
# Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
# Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
#
# This file is part of Stockfish.
#
# Stockfish is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
@@ -33,10 +31,9 @@ BINDIR = $(PREFIX)/bin
PGOBENCH = ./$(EXE) bench 32 1 10 default depth
### Object files
OBJS = bitboard.o pawns.o material.o endgame.o evaluate.o main.o \
misc.o move.o movegen.o history.o movepick.o search.o position.o \
tt.o uci.o ucioption.o book.o bitbase.o san.o benchmark.o timeman.o
OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \
material.o misc.o move.o movegen.o movepick.o pawns.o position.o \
search.o thread.o timeman.o tt.o uci.o ucioption.o
### ==========================================================================
### Section 2. High-level Configuration
@@ -230,11 +227,11 @@ endif
CXXFLAGS = -g -Wall -Wcast-qual -fno-exceptions -fno-rtti $(EXTRACXXFLAGS)
ifeq ($(comp),gcc)
CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra
CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra -Wshadow
endif
ifeq ($(comp),mingw)
CXXFLAGS += -Wno-long-long -Wextra
CXXFLAGS += -Wextra -Wshadow
endif
ifeq ($(comp),icc)
@@ -278,10 +275,10 @@ ifeq ($(optimize),yes)
endif
ifeq ($(comp),icc)
CXXFLAGS += -fast
ifeq ($(os),osx)
CXXFLAGS += -mdynamic-no-pic
CXXFLAGS += -fast -mdynamic-no-pic
else
CXXFLAGS += -O3
endif
endif
endif
+67 -68
View File
@@ -17,24 +17,17 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <fstream>
#include <iostream>
#include <vector>
#include "position.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
using namespace std;
////
//// Variables
////
static const string BenchmarkPositions[] = {
static const string Defaults[] = {
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -",
@@ -55,10 +48,6 @@ static const string BenchmarkPositions[] = {
};
////
//// Functions
////
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set
/// of positions for a given limit each. There are five parameters; the
/// transposition table size, the number of search threads that should
@@ -70,85 +59,95 @@ static const string BenchmarkPositions[] = {
void benchmark(int argc, char* argv[]) {
vector<string> positions;
string ttSize, threads, valStr, posFile, valType;
int val, secsPerPos, maxDepth, maxNodes;
vector<string> fenList;
SearchLimits limits;
int64_t totalNodes;
int time;
ttSize = argc > 2 ? argv[2] : "128";
threads = argc > 3 ? argv[3] : "1";
valStr = argc > 4 ? argv[4] : "12";
posFile = argc > 5 ? argv[5] : "default";
valType = argc > 6 ? argv[6] : "depth";
// Load default positions
for (int i = 0; !Defaults[i].empty(); i++)
fenList.push_back(Defaults[i]);
// Assign default values to missing arguments
string ttSize = argc > 2 ? argv[2] : "128";
string threads = argc > 3 ? argv[3] : "1";
string valStr = argc > 4 ? argv[4] : "12";
string fenFile = argc > 5 ? argv[5] : "default";
string valType = argc > 6 ? argv[6] : "depth";
Options["Hash"].set_value(ttSize);
Options["Threads"].set_value(threads);
Options["OwnBook"].set_value("false");
Options["Use Search Log"].set_value("true");
Options["Search Log Filename"].set_value("bench.txt");
secsPerPos = maxDepth = maxNodes = 0;
val = atoi(valStr.c_str());
if (valType == "depth" || valType == "perft")
maxDepth = val;
// Search should be limited by nodes, time or depth ?
if (valType == "nodes")
limits.maxNodes = atoi(valStr.c_str());
else if (valType == "time")
secsPerPos = val * 1000;
limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
else
maxNodes = val;
limits.maxDepth = atoi(valStr.c_str());
if (posFile != "default")
// Do we need to load positions from a given FEN file ?
if (fenFile != "default")
{
ifstream fenFile(posFile.c_str());
if (!fenFile.is_open())
string fen;
ifstream f(fenFile.c_str());
if (f.is_open())
{
cerr << "Unable to open positions file " << posFile << endl;
fenList.clear();
while (getline(f, fen))
if (!fen.empty())
fenList.push_back(fen);
f.close();
}
else
{
cerr << "Unable to open FEN file " << fenFile << endl;
exit(EXIT_FAILURE);
}
string pos;
while (fenFile.good())
{
getline(fenFile, pos);
if (!pos.empty())
positions.push_back(pos);
}
fenFile.close();
} else
for (int i = 0; !BenchmarkPositions[i].empty(); i++)
positions.push_back(BenchmarkPositions[i]);
}
vector<string>::iterator it;
int cnt = 1;
int64_t totalNodes = 0;
int startTime = get_system_time();
// Ok, let's start the benchmark !
totalNodes = 0;
time = get_system_time();
for (it = positions.begin(); it != positions.end(); ++it, ++cnt)
for (size_t i = 0; i < fenList.size(); i++)
{
Move moves[1] = { MOVE_NONE };
int dummy[2] = { 0, 0 };
Position pos(*it, 0);
cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl;
Move moves[] = { MOVE_NONE };
Position pos(fenList[i], false, 0);
cerr << "\nBench position: " << i + 1 << '/' << fenList.size() << endl;
if (valType == "perft")
{
int64_t perftCnt = perft(pos, maxDepth * ONE_PLY);
cerr << "\nPerft " << maxDepth << " result (nodes searched): " << perftCnt << endl << endl;
totalNodes += perftCnt;
} else {
if (!think(pos, false, false, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY);
totalNodes += cnt;
cerr << "\nPerft " << limits.maxDepth << " nodes counted: " << cnt << endl;
}
else
{
if (!think(pos, limits, moves))
break;
totalNodes += pos.nodes_searched();
}
}
cnt = get_system_time() - startTime;
cerr << "==============================="
<< "\nTotal time (ms) : " << cnt
<< "\nNodes searched : " << totalNodes
<< "\nNodes/second : " << (int)(totalNodes/(cnt/1000.0)) << endl << endl;
time = get_system_time() - time;
// Under MS Visual C++ debug window always unconditionally closes
// when program exits, this is bad because we want to read results before.
cerr << "\n==============================="
<< "\nTotal time (ms) : " << time
<< "\nNodes searched : " << totalNodes
<< "\nNodes/second : " << (int)(totalNodes / (time / 1000.0)) << endl << endl;
// MS Visual C++ debug window always unconditionally closes when program
// exits, this is bad because we want to read results before.
#if (defined(WINDOWS) || defined(WIN32) || defined(WIN64))
cerr << "Press any key to exit" << endl;
cin >> ttSize;
cin >> time;
#endif
}
+61 -96
View File
@@ -17,20 +17,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include "bitboard.h"
#include "square.h"
////
//// Local definitions
////
#include "types.h"
namespace {
@@ -47,35 +37,42 @@ namespace {
bool is_legal() const;
bool is_immediate_draw() const;
bool is_immediate_win() const;
Bitboard wk_attacks() const { return StepAttackBB[WK][whiteKingSquare]; }
Bitboard bk_attacks() const { return StepAttackBB[BK][blackKingSquare]; }
Bitboard pawn_attacks() const { return StepAttackBB[WP][pawnSquare]; }
Bitboard wk_attacks() const { return StepAttacksBB[WK][whiteKingSquare]; }
Bitboard bk_attacks() const { return StepAttacksBB[BK][blackKingSquare]; }
Bitboard pawn_attacks() const { return StepAttacksBB[WP][pawnSquare]; }
Square whiteKingSquare, blackKingSquare, pawnSquare;
Color sideToMove;
};
const int IndexMax = 2 * 24 * 64 * 64;
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
const int IndexMax = 2 * 24 * 64 * 64; // color * wp_sq * wk_sq * bk_sq
// Each uint32_t stores results of 32 positions, one per bit
uint32_t KPKBitbase[IndexMax / 32];
Result classify_wtm(const KPKPosition& pos, const Result bb[]);
Result classify_btm(const KPKPosition& pos, const Result bb[]);
int compute_index(Square wksq, Square bksq, Square psq, Color stm);
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm);
}
////
//// Functions
////
uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm) {
void generate_kpk_bitbase(uint8_t bitbase[]) {
int index = compute_index(wksq, bksq, wpsq, stm);
return KPKBitbase[index / 32] & (1 << (index & 31));
}
void init_kpk_bitbase() {
bool repeat;
int i, j, b;
KPKPosition pos;
Result bb[IndexMax];
KPKPosition pos;
bool repeat;
// Initialize table
for (i = 0; i < IndexMax; i++)
for (int i = 0; i < IndexMax; i++)
{
pos.from_index(i);
bb[i] = !pos.is_legal() ? RESULT_INVALID
@@ -87,7 +84,7 @@ void generate_kpk_bitbase(uint8_t bitbase[]) {
do {
repeat = false;
for (i = 0; i < IndexMax; i++)
for (int i = 0; i < IndexMax; i++)
if (bb[i] == RESULT_UNKNOWN)
{
pos.from_index(i);
@@ -100,29 +97,36 @@ void generate_kpk_bitbase(uint8_t bitbase[]) {
} while (repeat);
// Compress result and map into supplied bitbase parameter
for (i = 0; i < 24576; i++)
{
b = 0;
for (j = 0; j < 8; j++)
if (bb[8*i+j] == RESULT_WIN || bb[8*i+j] == RESULT_LOSS)
b |= (1 << j);
bitbase[i] = (uint8_t)b;
}
// Map 32 position results into one KPKBitbase[] entry
for (int i = 0; i < IndexMax / 32; i++)
for (int j = 0; j < 32; j++)
if (bb[32 * i + j] == RESULT_WIN || bb[32 * i + j] == RESULT_LOSS)
KPKBitbase[i] |= (1 << j);
}
namespace {
int compute_index(Square wksq, Square bksq, Square psq, Color stm) {
// A KPK bitbase index is an integer in [0, IndexMax] range
//
// Information is mapped in this way
//
// bit 0: side to move (WHITE or BLACK)
// bit 1- 6: black king square (from SQ_A1 to SQ_H8)
// bit 7-12: white king square (from SQ_A1 to SQ_H8)
// bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn rank - 1 (from RANK_2 - 1 to RANK_7 - 1)
int p = int(square_file(psq)) + (int(square_rank(psq)) - 1) * 4;
int r = int(stm) + 2 * int(bksq) + 128 * int(wksq) + 8192 * p;
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm) {
assert(r >= 0 && r < IndexMax);
assert(square_file(wpsq) <= FILE_D);
return r;
int p = int(square_file(wpsq)) + 4 * int(square_rank(wpsq) - 1);
int r = int(stm) + 2 * int(bksq) + 128 * int(wksq) + 8192 * p;
assert(r >= 0 && r < IndexMax);
return r;
}
void KPKPosition::from_index(int index) {
@@ -139,7 +143,7 @@ namespace {
if ( whiteKingSquare == pawnSquare
|| whiteKingSquare == blackKingSquare
|| pawnSquare == blackKingSquare)
|| blackKingSquare == pawnSquare)
return false;
if (sideToMove == WHITE)
@@ -171,7 +175,7 @@ namespace {
}
else
{
// Case 1: Stalemate
// Case 1: Stalemate (possible pawn files are only from A to D)
if ( whiteKingSquare == SQ_A8
&& pawnSquare == SQ_A7
&& (blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8))
@@ -186,6 +190,7 @@ namespace {
// white pawn can be promoted without getting captured.
return sideToMove == WHITE
&& square_rank(pawnSquare) == RANK_7
&& whiteKingSquare != pawnSquare + DELTA_N
&& ( square_distance(blackKingSquare, pawnSquare + DELTA_N) > 1
|| bit_is_set(wk_attacks(), pawnSquare + DELTA_N));
}
@@ -200,77 +205,47 @@ namespace {
bool unknownFound = false;
Bitboard b;
Square s;
int idx;
Result r;
// King moves
b = pos.wk_attacks();
while (b)
{
s = pop_1st_bit(&b);
idx = compute_index(s, pos.blackKingSquare, pos.pawnSquare, BLACK);
r = bb[compute_index(s, pos.blackKingSquare, pos.pawnSquare, BLACK)];
switch (bb[idx]) {
case RESULT_LOSS:
if (r == RESULT_LOSS)
return RESULT_WIN;
case RESULT_UNKNOWN:
if (r == RESULT_UNKNOWN)
unknownFound = true;
case RESULT_DRAW:
case RESULT_INVALID:
break;
default:
assert(false);
}
}
// Pawn moves
if (square_rank(pos.pawnSquare) < RANK_7)
{
s = pos.pawnSquare + DELTA_N;
idx = compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK);
r = bb[compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK)];
switch (bb[idx]) {
case RESULT_LOSS:
if (r == RESULT_LOSS)
return RESULT_WIN;
case RESULT_UNKNOWN:
if (r == RESULT_UNKNOWN)
unknownFound = true;
case RESULT_DRAW:
case RESULT_INVALID:
break;
default:
assert(false);
}
// Double pawn push
if ( square_rank(s) == RANK_3
&& s != pos.whiteKingSquare
&& s != pos.blackKingSquare)
{
s += DELTA_N;
idx = compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK);
r = bb[compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK)];
switch (bb[idx]) {
case RESULT_LOSS:
if (r == RESULT_LOSS)
return RESULT_WIN;
case RESULT_UNKNOWN:
if (r == RESULT_UNKNOWN)
unknownFound = true;
case RESULT_DRAW:
case RESULT_INVALID:
break;
default:
assert(false);
}
}
}
return unknownFound ? RESULT_UNKNOWN : RESULT_DRAW;
@@ -288,30 +263,20 @@ namespace {
bool unknownFound = false;
Bitboard b;
Square s;
int idx;
Result r;
// King moves
b = pos.bk_attacks();
while (b)
{
s = pop_1st_bit(&b);
idx = compute_index(pos.whiteKingSquare, s, pos.pawnSquare, WHITE);
r = bb[compute_index(pos.whiteKingSquare, s, pos.pawnSquare, WHITE)];
switch (bb[idx]) {
case RESULT_DRAW:
if (r == RESULT_DRAW)
return RESULT_DRAW;
case RESULT_UNKNOWN:
if (r == RESULT_UNKNOWN)
unknownFound = true;
case RESULT_WIN:
case RESULT_INVALID:
break;
default:
assert(false);
}
}
return unknownFound ? RESULT_UNKNOWN : RESULT_LOSS;
}
+213 -274
View File
@@ -17,67 +17,61 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <iostream>
#include "bitboard.h"
#include "bitcount.h"
#if defined(IS_64BIT)
const uint64_t BMult[64] = {
0x440049104032280ULL, 0x1021023c82008040ULL, 0x404040082000048ULL,
0x48c4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL,
0x181011002e06040ULL, 0x9101004104200e00ULL, 0x1240848848310401ULL,
0x2000142828050024ULL, 0x1004024d5000ULL, 0x102044400800200ULL,
0x8108108820112000ULL, 0xa880818210c00046ULL, 0x4008008801082000ULL,
0x60882404049400ULL, 0x104402004240810ULL, 0xa002084250200ULL,
0x100b0880801100ULL, 0x4080201220101ULL, 0x44008080a00000ULL,
0x202200842000ULL, 0x5006004882d00808ULL, 0x200045080802ULL,
0x86100020200601ULL, 0xa802080a20112c02ULL, 0x80411218080900ULL,
0x200a0880080a0ULL, 0x9a01010000104000ULL, 0x28008003100080ULL,
0x211021004480417ULL, 0x401004188220806ULL, 0x825051400c2006ULL,
0x140c0210943000ULL, 0x242800300080ULL, 0xc2208120080200ULL,
0x0440049104032280ULL, 0x1021023C82008040ULL, 0x0404040082000048ULL,
0x48C4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL,
0x0181011002E06040ULL, 0x9101004104200E00ULL, 0x1240848848310401ULL,
0x2000142828050024ULL, 0x00001004024D5000ULL, 0x0102044400800200ULL,
0x8108108820112000ULL, 0xA880818210C00046ULL, 0x4008008801082000ULL,
0x0060882404049400ULL, 0x0104402004240810ULL, 0x000A002084250200ULL,
0x00100B0880801100ULL, 0x0004080201220101ULL, 0x0044008080A00000ULL,
0x0000202200842000ULL, 0x5006004882D00808ULL, 0x0000200045080802ULL,
0x0086100020200601ULL, 0xA802080A20112C02ULL, 0x0080411218080900ULL,
0x000200A0880080A0ULL, 0x9A01010000104000ULL, 0x0028008003100080ULL,
0x0211021004480417ULL, 0x0401004188220806ULL, 0x00825051400C2006ULL,
0x00140C0210943000ULL, 0x0000242800300080ULL, 0x00C2208120080200ULL,
0x2430008200002200ULL, 0x1010100112008040ULL, 0x8141050100020842ULL,
0x822081014405ULL, 0x800c049e40400804ULL, 0x4a0404028a000820ULL,
0x22060201041200ULL, 0x360904200840801ULL, 0x881a08208800400ULL,
0x60202c00400420ULL, 0x1204440086061400ULL, 0x8184042804040ULL,
0x64040315300400ULL, 0xc01008801090a00ULL, 0x808010401140c00ULL,
0x4004830c2020040ULL, 0x80005002020054ULL, 0x40000c14481a0490ULL,
0x10500101042048ULL, 0x1010100200424000ULL, 0x640901901040ULL,
0xa0201014840ULL, 0x840082aa011002ULL, 0x10010840084240aULL,
0x420400810420608ULL, 0x8d40230408102100ULL, 0x4a00200612222409ULL,
0xa08520292120600ULL
0x0000822081014405ULL, 0x800C049E40400804ULL, 0x4A0404028A000820ULL,
0x0022060201041200ULL, 0x0360904200840801ULL, 0x0881A08208800400ULL,
0x0060202C00400420ULL, 0x1204440086061400ULL, 0x0008184042804040ULL,
0x0064040315300400ULL, 0x0C01008801090A00ULL, 0x0808010401140C00ULL,
0x04004830C2020040ULL, 0x0080005002020054ULL, 0x40000C14481A0490ULL,
0x0010500101042048ULL, 0x1010100200424000ULL, 0x0000640901901040ULL,
0x00000A0201014840ULL, 0x00840082AA011002ULL, 0x010010840084240AULL,
0x0420400810420608ULL, 0x8D40230408102100ULL, 0x4A00200612222409ULL,
0x0A08520292120600ULL
};
const uint64_t RMult[64] = {
0xa8002c000108020ULL, 0x4440200140003000ULL, 0x8080200010011880ULL,
0x380180080141000ULL, 0x1a00060008211044ULL, 0x410001000a0c0008ULL,
0x9500060004008100ULL, 0x100024284a20700ULL, 0x802140008000ULL,
0x80c01002a00840ULL, 0x402004282011020ULL, 0x9862000820420050ULL,
0x1001448011100ULL, 0x6432800200800400ULL, 0x40100010002000cULL,
0x2800d0010c080ULL, 0x90c0008000803042ULL, 0x4010004000200041ULL,
0x3010010200040ULL, 0xa40828028001000ULL, 0x123010008000430ULL,
0x24008004020080ULL, 0x60040001104802ULL, 0x582200028400d1ULL,
0x4000802080044000ULL, 0x408208200420308ULL, 0x610038080102000ULL,
0x3601000900100020ULL, 0x80080040180ULL, 0xc2020080040080ULL,
0x80084400100102ULL, 0x4022408200014401ULL, 0x40052040800082ULL,
0xb08200280804000ULL, 0x8a80a008801000ULL, 0x4000480080801000ULL,
0x911808800801401ULL, 0x822a003002001894ULL, 0x401068091400108aULL,
0x4a10a00004cULL, 0x2000800640008024ULL, 0x1486408102020020ULL,
0x100a000d50041ULL, 0x810050020b0020ULL, 0x204000800808004ULL,
0x20048100a000cULL, 0x112000831020004ULL, 0x9000040810002ULL,
0x440490200208200ULL, 0x8910401000200040ULL, 0x6404200050008480ULL,
0x4b824a2010010100ULL, 0x4080801810c0080ULL, 0x400802a0080ULL,
0x8224080110026400ULL, 0x40002c4104088200ULL, 0x1002100104a0282ULL,
0x1208400811048021ULL, 0x3201014a40d02001ULL, 0x5100019200501ULL,
0x101000208001005ULL, 0x2008450080702ULL, 0x1002080301d00cULL,
0x410201ce5c030092ULL
0x0A8002C000108020ULL, 0x4440200140003000ULL, 0x8080200010011880ULL,
0x0380180080141000ULL, 0x1A00060008211044ULL, 0x410001000A0C0008ULL,
0x9500060004008100ULL, 0x0100024284A20700ULL, 0x0000802140008000ULL,
0x0080C01002A00840ULL, 0x0402004282011020ULL, 0x9862000820420050ULL,
0x0001001448011100ULL, 0x6432800200800400ULL, 0x040100010002000CULL,
0x0002800D0010C080ULL, 0x90C0008000803042ULL, 0x4010004000200041ULL,
0x0003010010200040ULL, 0x0A40828028001000ULL, 0x0123010008000430ULL,
0x0024008004020080ULL, 0x0060040001104802ULL, 0x00582200028400D1ULL,
0x4000802080044000ULL, 0x0408208200420308ULL, 0x0610038080102000ULL,
0x3601000900100020ULL, 0x0000080080040180ULL, 0x00C2020080040080ULL,
0x0080084400100102ULL, 0x4022408200014401ULL, 0x0040052040800082ULL,
0x0B08200280804000ULL, 0x008A80A008801000ULL, 0x4000480080801000ULL,
0x0911808800801401ULL, 0x822A003002001894ULL, 0x401068091400108AULL,
0x000004A10A00004CULL, 0x2000800640008024ULL, 0x1486408102020020ULL,
0x000100A000D50041ULL, 0x00810050020B0020ULL, 0x0204000800808004ULL,
0x00020048100A000CULL, 0x0112000831020004ULL, 0x0009000040810002ULL,
0x0440490200208200ULL, 0x8910401000200040ULL, 0x6404200050008480ULL,
0x4B824A2010010100ULL, 0x04080801810C0080ULL, 0x00000400802A0080ULL,
0x8224080110026400ULL, 0x40002C4104088200ULL, 0x01002100104A0282ULL,
0x1208400811048021ULL, 0x3201014A40D02001ULL, 0x0005100019200501ULL,
0x0101000208001005ULL, 0x0002008450080702ULL, 0x001002080301D00CULL,
0x410201CE5C030092ULL
};
const int BShift[64] = {
@@ -97,53 +91,53 @@ const int RShift[64] = {
#else // if !defined(IS_64BIT)
const uint64_t BMult[64] = {
0x54142844c6a22981ULL, 0x710358a6ea25c19eULL, 0x704f746d63a4a8dcULL,
0xbfed1a0b80f838c5ULL, 0x90561d5631e62110ULL, 0x2804260376e60944ULL,
0x84a656409aa76871ULL, 0xf0267f64c28b6197ULL, 0x70764ebb762f0585ULL,
0x92aa09e0cfe161deULL, 0x41ee1f6bb266f60eULL, 0xddcbf04f6039c444ULL,
0x5a3fab7bac0d988aULL, 0xd3727877fa4eaa03ULL, 0xd988402d868ddaaeULL,
0x812b291afa075c7cULL, 0x94faf987b685a932ULL, 0x3ed867d8470d08dbULL,
0x92517660b8901de8ULL, 0x2d97e43e058814b4ULL, 0x880a10c220b25582ULL,
0xc7c6520d1f1a0477ULL, 0xdbfc7fbcd7656aa6ULL, 0x78b1b9bfb1a2b84fULL,
0x2f20037f112a0bc1ULL, 0x657171ea2269a916ULL, 0xc08302b07142210eULL,
0x880a4403064080bULL, 0x3602420842208c00ULL, 0x852800dc7e0b6602ULL,
0x595a3fbbaa0f03b2ULL, 0x9f01411558159d5eULL, 0x2b4a4a5f88b394f2ULL,
0x4afcbffc292dd03aULL, 0x4a4094a3b3f10522ULL, 0xb06f00b491f30048ULL,
0xd5b3820280d77004ULL, 0x8b2e01e7c8e57a75ULL, 0x2d342794e886c2e6ULL,
0xc302c410cde21461ULL, 0x111f426f1379c274ULL, 0xe0569220abb31588ULL,
0x5026d3064d453324ULL, 0xe2076040c343cd8aULL, 0x93efd1e1738021eeULL,
0xb680804bed143132ULL, 0x44e361b21986944cULL, 0x44c60170ef5c598cULL,
0xf4da475c195c9c94ULL, 0xa3afbb5f72060b1dULL, 0xbc75f410e41c4ffcULL,
0xb51c099390520922ULL, 0x902c011f8f8ec368ULL, 0x950b56b3d6f5490aULL,
0x3909e0635bf202d0ULL, 0x5744f90206ec10ccULL, 0xdc59fd76317abbc1ULL,
0x881c7c67fcbfc4f6ULL, 0x47ca41e7e440d423ULL, 0xeb0c88112048d004ULL,
0x51c60e04359aef1aULL, 0x1aa1fe0e957a5554ULL, 0xdd9448db4f5e3104ULL,
0xdc01f6dca4bebbdcULL,
0x54142844C6A22981ULL, 0x710358A6EA25C19EULL, 0x704F746D63A4A8DCULL,
0xBFED1A0B80F838C5ULL, 0x90561D5631E62110ULL, 0x2804260376E60944ULL,
0x84A656409AA76871ULL, 0xF0267F64C28B6197ULL, 0x70764EBB762F0585ULL,
0x92AA09E0CFE161DEULL, 0x41EE1F6BB266F60EULL, 0xDDCBF04F6039C444ULL,
0x5A3FAB7BAC0D988AULL, 0xD3727877FA4EAA03ULL, 0xD988402D868DDAAEULL,
0x812B291AFA075C7CULL, 0x94FAF987B685A932ULL, 0x3ED867D8470D08DBULL,
0x92517660B8901DE8ULL, 0x2D97E43E058814B4ULL, 0x880A10C220B25582ULL,
0xC7C6520D1F1A0477ULL, 0xDBFC7FBCD7656AA6ULL, 0x78B1B9BFB1A2B84FULL,
0x2F20037F112A0BC1ULL, 0x657171EA2269A916ULL, 0xC08302B07142210EULL,
0x0880A4403064080BULL, 0x3602420842208C00ULL, 0x852800DC7E0B6602ULL,
0x595A3FBBAA0F03B2ULL, 0x9F01411558159D5EULL, 0x2B4A4A5F88B394F2ULL,
0x4AFCBFFC292DD03AULL, 0x4A4094A3B3F10522ULL, 0xB06F00B491F30048ULL,
0xD5B3820280D77004ULL, 0x8B2E01E7C8E57A75ULL, 0x2D342794E886C2E6ULL,
0xC302C410CDE21461ULL, 0x111F426F1379C274ULL, 0xE0569220ABB31588ULL,
0x5026D3064D453324ULL, 0xE2076040C343CD8AULL, 0x93EFD1E1738021EEULL,
0xB680804BED143132ULL, 0x44E361B21986944CULL, 0x44C60170EF5C598CULL,
0xF4DA475C195C9C94ULL, 0xA3AFBB5F72060B1DULL, 0xBC75F410E41C4FFCULL,
0xB51C099390520922ULL, 0x902C011F8F8EC368ULL, 0x950B56B3D6F5490AULL,
0x3909E0635BF202D0ULL, 0x5744F90206EC10CCULL, 0xDC59FD76317ABBC1ULL,
0x881C7C67FCBFC4F6ULL, 0x47CA41E7E440D423ULL, 0xEB0C88112048D004ULL,
0x51C60E04359AEF1AULL, 0x1AA1FE0E957A5554ULL, 0xDD9448DB4F5E3104ULL,
0xDC01F6DCA4BEBBDCULL,
};
const uint64_t RMult[64] = {
0xd7445cdec88002c0ULL, 0xd0a505c1f2001722ULL, 0xe065d1c896002182ULL,
0x9a8c41e75a000892ULL, 0x8900b10c89002aa8ULL, 0x9b28d1c1d60005a2ULL,
0x15d6c88de002d9aULL, 0xb1dbfc802e8016a9ULL, 0x149a1042d9d60029ULL,
0xb9c08050599e002fULL, 0x132208c3af300403ULL, 0xc1000ce2e9c50070ULL,
0x9d9aa13c99020012ULL, 0xb6b078daf71e0046ULL, 0x9d880182fb6e002eULL,
0x52889f467e850037ULL, 0xda6dc008d19a8480ULL, 0x468286034f902420ULL,
0x7140ac09dc54c020ULL, 0xd76ffffa39548808ULL, 0xea901c4141500808ULL,
0xc91004093f953a02ULL, 0x2882afa8f6bb402ULL, 0xaebe335692442c01ULL,
0xe904a22079fb91eULL, 0x13a514851055f606ULL, 0x76c782018c8fe632ULL,
0x1dc012a9d116da06ULL, 0x3c9e0037264fffa6ULL, 0x2036002853c6e4a2ULL,
0xe3fe08500afb47d4ULL, 0xf38af25c86b025c2ULL, 0xc0800e2182cf9a40ULL,
0x72002480d1f60673ULL, 0x2500200bae6e9b53ULL, 0xc60018c1eefca252ULL,
0x600590473e3608aULL, 0x46002c4ab3fe51b2ULL, 0xa200011486bcc8d2ULL,
0xb680078095784c63ULL, 0x2742002639bf11aeULL, 0xc7d60021a5bdb142ULL,
0xc8c04016bb83d820ULL, 0xbd520028123b4842ULL, 0x9d1600344ac2a832ULL,
0x6a808005631c8a05ULL, 0x604600a148d5389aULL, 0xe2e40103d40dea65ULL,
0x945b5a0087c62a81ULL, 0x12dc200cd82d28eULL, 0x2431c600b5f9ef76ULL,
0xfb142a006a9b314aULL, 0x6870e00a1c97d62ULL, 0x2a9db2004a2689a2ULL,
0xd3594600caf5d1a2ULL, 0xee0e4900439344a7ULL, 0x89c4d266ca25007aULL,
0x3e0013a2743f97e3ULL, 0x180e31a0431378aULL, 0x3a9e465a4d42a512ULL,
0x98d0a11a0c0d9cc2ULL, 0x8e711c1aba19b01eULL, 0x8dcdc836dd201142ULL,
0x5ac08a4735370479ULL,
0xD7445CDEC88002C0ULL, 0xD0A505C1F2001722ULL, 0xE065D1C896002182ULL,
0x9A8C41E75A000892ULL, 0x8900B10C89002AA8ULL, 0x9B28D1C1D60005A2ULL,
0x015D6C88DE002D9AULL, 0xB1DBFC802E8016A9ULL, 0x149A1042D9D60029ULL,
0xB9C08050599E002FULL, 0x132208C3AF300403ULL, 0xC1000CE2E9C50070ULL,
0x9D9AA13C99020012ULL, 0xB6B078DAF71E0046ULL, 0x9D880182FB6E002EULL,
0x52889F467E850037ULL, 0xDA6DC008D19A8480ULL, 0x468286034F902420ULL,
0x7140AC09DC54C020ULL, 0xD76FFFFA39548808ULL, 0xEA901C4141500808ULL,
0xC91004093F953A02ULL, 0x02882AFA8F6BB402ULL, 0xAEBE335692442C01ULL,
0x0E904A22079FB91EULL, 0x13A514851055F606ULL, 0x76C782018C8FE632ULL,
0x1DC012A9D116DA06ULL, 0x3C9E0037264FFFA6ULL, 0x2036002853C6E4A2ULL,
0xE3FE08500AFB47D4ULL, 0xF38AF25C86B025C2ULL, 0xC0800E2182CF9A40ULL,
0x72002480D1F60673ULL, 0x2500200BAE6E9B53ULL, 0xC60018C1EEFCA252ULL,
0x0600590473E3608AULL, 0x46002C4AB3FE51B2ULL, 0xA200011486BCC8D2ULL,
0xB680078095784C63ULL, 0x2742002639BF11AEULL, 0xC7D60021A5BDB142ULL,
0xC8C04016BB83D820ULL, 0xBD520028123B4842ULL, 0x9D1600344AC2A832ULL,
0x6A808005631C8A05ULL, 0x604600A148D5389AULL, 0xE2E40103D40DEA65ULL,
0x945B5A0087C62A81ULL, 0x012DC200CD82D28EULL, 0x2431C600B5F9EF76ULL,
0xFB142A006A9B314AULL, 0x06870E00A1C97D62ULL, 0x2A9DB2004A2689A2ULL,
0xD3594600CAF5D1A2ULL, 0xEE0E4900439344A7ULL, 0x89C4D266CA25007AULL,
0x3E0013A2743F97E3ULL, 0x0180E31A0431378AULL, 0x3A9E465A4D42A512ULL,
0x98D0A11A0C0D9CC2ULL, 0x8E711C1ABA19B01EULL, 0x8DCDC836DD201142ULL,
0x5AC08A4735370479ULL,
};
const int BShift[64] = {
@@ -162,57 +156,8 @@ const int RShift[64] = {
#endif // defined(IS_64BIT)
const Bitboard LightSquaresBB = 0x55AA55AA55AA55AAULL;
const Bitboard DarkSquaresBB = 0xAA55AA55AA55AA55ULL;
const Bitboard SquaresByColorBB[2] = { DarkSquaresBB, LightSquaresBB };
const Bitboard FileBB[8] = {
FileABB, FileBBB, FileCBB, FileDBB, FileEBB, FileFBB, FileGBB, FileHBB
};
const Bitboard NeighboringFilesBB[8] = {
FileBBB, FileABB|FileCBB, FileBBB|FileDBB, FileCBB|FileEBB,
FileDBB|FileFBB, FileEBB|FileGBB, FileFBB|FileHBB, FileGBB
};
const Bitboard ThisAndNeighboringFilesBB[8] = {
FileABB|FileBBB, FileABB|FileBBB|FileCBB,
FileBBB|FileCBB|FileDBB, FileCBB|FileDBB|FileEBB,
FileDBB|FileEBB|FileFBB, FileEBB|FileFBB|FileGBB,
FileFBB|FileGBB|FileHBB, FileGBB|FileHBB
};
const Bitboard RankBB[8] = {
Rank1BB, Rank2BB, Rank3BB, Rank4BB, Rank5BB, Rank6BB, Rank7BB, Rank8BB
};
const Bitboard RelativeRankBB[2][8] = {
{ Rank1BB, Rank2BB, Rank3BB, Rank4BB, Rank5BB, Rank6BB, Rank7BB, Rank8BB },
{ Rank8BB, Rank7BB, Rank6BB, Rank5BB, Rank4BB, Rank3BB, Rank2BB, Rank1BB }
};
const Bitboard InFrontBB[2][8] = {
{ Rank2BB | Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank6BB | Rank7BB | Rank8BB,
Rank7BB | Rank8BB,
Rank8BB,
EmptyBoardBB
},
{ EmptyBoardBB,
Rank1BB,
Rank2BB | Rank1BB,
Rank3BB | Rank2BB | Rank1BB,
Rank4BB | Rank3BB | Rank2BB | Rank1BB,
Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB,
Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB,
Rank7BB | Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB
}
};
// Global bitboards definitions with static storage duration are
// automatically set to zero before enter main().
Bitboard RMask[64];
int RAttackIndex[64];
Bitboard RAttacks[0x19000];
@@ -224,9 +169,14 @@ Bitboard BAttacks[0x1480];
Bitboard SetMaskBB[65];
Bitboard ClearMaskBB[65];
Bitboard StepAttackBB[16][64];
Bitboard SquaresByColorBB[2];
Bitboard FileBB[8];
Bitboard RankBB[8];
Bitboard NeighboringFilesBB[8];
Bitboard ThisAndNeighboringFilesBB[8];
Bitboard InFrontBB[2][8];
Bitboard StepAttacksBB[16][64];
Bitboard BetweenBB[64][64];
Bitboard SquaresInFrontMask[2][64];
Bitboard PassedPawnMask[2][64];
Bitboard AttackSpanMask[2][64];
@@ -238,63 +188,37 @@ Bitboard QueenPseudoAttacks[64];
uint8_t BitCount8Bit[256];
////
//// Local definitions
////
namespace {
void init_masks();
void init_attacks();
void init_between_bitboards();
void init_step_attacks();
void init_pseudo_attacks();
SquareDelta squares_delta(Square orig, Square dest);
void init_between_bitboards();
Bitboard index_to_bitboard(int index, Bitboard mask);
Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
Bitboard sliding_attacks(int sq, Bitboard occupied, int deltas[][2],
int fmin, int fmax, int rmin, int rmax);
void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[],
const int shift[], const Bitboard mult[], int deltas[][2]);
}
////
//// Functions
////
/// print_bitboard() prints a bitboard in an easily readable format to the
/// standard output. This is sometimes useful for debugging.
/// standard output. This is sometimes useful for debugging.
void print_bitboard(Bitboard b) {
for (Rank r = RANK_8; r >= RANK_1; r--)
{
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
for (File f = FILE_A; f <= FILE_H; f++)
std::cout << "| " << (bit_is_set(b, make_square(f, r))? 'X' : ' ') << ' ';
std::cout << "| " << (bit_is_set(b, make_square(f, r)) ? 'X' : ' ') << ' ';
std::cout << "|" << std::endl;
std::cout << "|\n";
}
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
}
/// init_bitboards() initializes various bitboard arrays. It is called during
/// program initialization.
void init_bitboards() {
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
init_masks();
init_attacks();
init_between_bitboards();
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas);
init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift, BMult, bishopDeltas);
init_pseudo_attacks();
}
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard.
@@ -303,10 +227,10 @@ void init_bitboards() {
static CACHE_LINE_ALIGNMENT
const int BitTable[64] = {
0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 20, 40, 5, 17, 26, 38, 15,
46, 29, 48, 10, 31, 35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27, 33, 39,
16, 37, 45, 47, 30, 53, 49, 56, 62, 11, 23, 32, 36, 44, 52, 55, 61, 22, 43,
51, 60, 42, 59, 58
0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 20, 40, 5, 17, 26,
38, 15, 46, 29, 48, 10, 31, 35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27,
33, 39, 16, 37, 45, 47, 30, 53, 49, 56, 62, 11, 23, 32, 36, 44, 52, 55, 61,
22, 43, 51, 60, 42, 59, 58
};
Square first_1(Bitboard b) {
@@ -323,10 +247,10 @@ Square pop_1st_bit(Bitboard* b) {
static CACHE_LINE_ALIGNMENT
const int BitTable[64] = {
63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5, 52,
26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38, 28,
58, 20, 37, 17, 36, 8
63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5,
52, 26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38,
28, 58, 20, 37, 17, 36, 8
};
Square first_1(Bitboard b) {
@@ -371,9 +295,25 @@ Square pop_1st_bit(Bitboard* bb) {
return ret;
}
#endif
#endif // !defined(USE_BSFQ)
/// init_bitboards() initializes various bitboard arrays. It is called during
/// program initialization.
void init_bitboards() {
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
init_masks();
init_step_attacks();
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas);
init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift, BMult, bishopDeltas);
init_pseudo_attacks();
init_between_bitboards();
}
namespace {
// All functions below are used to precompute various bitboards during
@@ -383,7 +323,31 @@ namespace {
void init_masks() {
SetMaskBB[SQ_NONE] = 0ULL;
SquaresByColorBB[DARK] = 0xAA55AA55AA55AA55ULL;
SquaresByColorBB[LIGHT] = ~SquaresByColorBB[DARK];
FileBB[FILE_A] = FileABB;
RankBB[RANK_1] = Rank1BB;
for (int f = FILE_B; f <= FILE_H; f++)
{
FileBB[f] = FileBB[f - 1] << 1;
RankBB[f] = RankBB[f - 1] << 8;
}
for (int f = FILE_A; f <= FILE_H; f++)
{
NeighboringFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndNeighboringFilesBB[f] = FileBB[f] | NeighboringFilesBB[f];
}
for (int rw = RANK_7, rb = RANK_2; rw >= RANK_1; rw--, rb++)
{
InFrontBB[WHITE][rw] = InFrontBB[WHITE][rw + 1] | RankBB[rw + 1];
InFrontBB[BLACK][rb] = InFrontBB[BLACK][rb - 1] | RankBB[rb - 1];
}
SetMaskBB[SQ_NONE] = EmptyBoardBB;
ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE];
for (Square s = SQ_A1; s <= SQ_H8; s++)
@@ -404,131 +368,87 @@ namespace {
BitCount8Bit[b] = (uint8_t)count_1s<CNT32>(b);
}
void init_attacks() {
void init_step_attacks() {
const int step[16][8] = {
const int step[][9] = {
{0},
{7,9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0},
{9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}, {0}, {0},
{-7,-9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0},
{9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}
{7,9,0}, {17,15,10,6,-6,-10,-15,-17,0}, {0}, {0}, {0},
{9,7,-7,-9,8,1,-1,-8,0}, {0}, {0},
{-7,-9,0}, {17,15,10,6,-6,-10,-15,-17,0}, {0}, {0}, {0},
{9,7,-7,-9,8,1,-1,-8,0}
};
for (int i = 0; i < 64; i++)
for (int j = 0; j <= int(BK); j++)
{
StepAttackBB[j][i] = EmptyBoardBB;
for (int k = 0; k < 8 && step[j][k] != 0; k++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
for (Piece pc = WP; pc <= BK; pc++)
for (int k = 0; step[pc][k] != 0; k++)
{
int l = i + step[j][k];
if (l >= 0 && l < 64 && abs((i & 7) - (l & 7)) < 3)
StepAttackBB[j][i] |= (1ULL << l);
Square to = s + Square(step[pc][k]);
if (square_is_ok(to) && square_distance(s, to) < 3)
set_bit(&StepAttacksBB[pc][s], to);
}
}
}
Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
int fmin=0, int fmax=7, int rmin=0, int rmax=7) {
Bitboard result = 0ULL;
Bitboard sliding_attacks(int sq, Bitboard occupied, int deltas[][2],
int fmin, int fmax, int rmin, int rmax) {
int dx, dy, f, r;
int rk = sq / 8;
int fl = sq % 8;
Bitboard attacks = EmptyBoardBB;
for (int i = 0; i < dirs; i++)
for (int i = 0; i < 4; i++)
{
int dx = deltas[i][0];
int dy = deltas[i][1];
int f = fl + dx;
int r = rk + dy;
dx = deltas[i][0];
dy = deltas[i][1];
f = fl + dx;
r = rk + dy;
while ( (dx == 0 || (f >= fmin && f <= fmax))
&& (dy == 0 || (r >= rmin && r <= rmax)))
{
result |= (1ULL << (f + r*8));
if (block & (1ULL << (f + r*8)))
attacks |= (1ULL << (f + r * 8));
if (occupied & (1ULL << (f + r * 8)))
break;
f += dx;
r += dy;
}
}
return result;
}
SquareDelta squares_delta(Square orig, Square dest) {
const SquareDelta deltas[] = { DELTA_N, DELTA_NE, DELTA_E, DELTA_SE,
DELTA_S, DELTA_SW, DELTA_W, DELTA_NW };
for (int idx = 0; idx < 8; idx++)
{
Square s = orig + deltas[idx];
while (square_is_ok(s) && square_distance(s, s - deltas[idx]) == 1)
{
if (s == dest)
return deltas[idx];
s += deltas[idx];
}
}
return DELTA_NONE;
}
void init_between_bitboards() {
Square s1, s2, s3;
SquareDelta d;
for (s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (s2 = SQ_A1; s2 <= SQ_H8; s2++)
{
BetweenBB[s1][s2] = EmptyBoardBB;
d = squares_delta(s1, s2);
if (d != DELTA_NONE)
for (s3 = s1 + d; s3 != s2; s3 += d)
set_bit(&(BetweenBB[s1][s2]), s3);
}
return attacks;
}
Bitboard index_to_bitboard(int index, Bitboard mask) {
Bitboard result = 0ULL;
int bits = count_1s<CNT32>(mask);
Bitboard result = EmptyBoardBB;
int sq, cnt = 0;
for (int i = 0; i < bits; i++)
while (mask)
{
int j = pop_1st_bit(&mask);
if (index & (1 << i))
result |= (1ULL << j);
sq = pop_1st_bit(&mask);
if (index & (1 << cnt++))
result |= (1ULL << sq);
}
return result;
}
void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[],
const int shift[], const Bitboard mult[], int deltas[][2]) {
Bitboard b, v;
int i, j, index;
for (int i = 0, index = 0; i < 64; i++)
for (i = index = 0; i < 64; i++)
{
attackIndex[i] = index;
mask[i] = sliding_attacks(i, 0, 4, deltas, 1, 6, 1, 6);
#if defined(IS_64BIT)
int j = (1 << (64 - shift[i]));
#else
int j = (1 << (32 - shift[i]));
#endif
mask[i] = sliding_attacks(i, 0, deltas, 1, 6, 1, 6);
j = 1 << ((CpuIs64Bit ? 64 : 32) - shift[i]);
for (int k = 0; k < j; k++)
{
#if defined(IS_64BIT)
Bitboard b = index_to_bitboard(k, mask[i]);
attacks[index + ((b * mult[i]) >> shift[i])] = sliding_attacks(i, b, 4, deltas);
#else
Bitboard b = index_to_bitboard(k, mask[i]);
unsigned v = int(b) * int(mult[i]) ^ int(b >> 32) * int(mult[i] >> 32);
attacks[index + (v >> shift[i])] = sliding_attacks(i, b, 4, deltas);
#endif
b = index_to_bitboard(k, mask[i]);
v = CpuIs64Bit ? b * mult[i] : unsigned(b * mult[i] ^ (b >> 32) * (mult[i] >> 32));
attacks[index + (v >> shift[i])] = sliding_attacks(i, b, deltas, 0, 7, 0, 7);
}
index += j;
}
@@ -544,4 +464,23 @@ namespace {
}
}
void init_between_bitboards() {
Square s1, s2, s3, d;
int f, r;
for (s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (bit_is_set(QueenPseudoAttacks[s1], s2))
{
f = file_distance(s1, s2);
r = rank_distance(s1, s2);
d = (s2 - s1) / Max(f, r);
for (s3 = s1 + d; s3 != s2; s3 += d)
set_bit(&(BetweenBB[s1][s2]), s3);
}
}
}
+30 -78
View File
@@ -18,23 +18,11 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(BITBOARD_H_INCLUDED)
#define BITBOARD_H_INCLUDED
////
//// Includes
////
#include "piece.h"
#include "square.h"
#include "types.h"
////
//// Constants and variables
////
const Bitboard EmptyBoardBB = 0;
const Bitboard FileABB = 0x0101010101010101ULL;
@@ -55,18 +43,17 @@ const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
extern const Bitboard SquaresByColorBB[2];
extern const Bitboard FileBB[8];
extern const Bitboard NeighboringFilesBB[8];
extern const Bitboard ThisAndNeighboringFilesBB[8];
extern const Bitboard RankBB[8];
extern const Bitboard RelativeRankBB[2][8];
extern const Bitboard InFrontBB[2][8];
extern Bitboard SquaresByColorBB[2];
extern Bitboard FileBB[8];
extern Bitboard NeighboringFilesBB[8];
extern Bitboard ThisAndNeighboringFilesBB[8];
extern Bitboard RankBB[8];
extern Bitboard InFrontBB[2][8];
extern Bitboard SetMaskBB[65];
extern Bitboard ClearMaskBB[65];
extern Bitboard StepAttackBB[16][64];
extern Bitboard StepAttacksBB[16][64];
extern Bitboard BetweenBB[64][64];
extern Bitboard SquaresInFrontMask[2][64];
@@ -92,10 +79,6 @@ extern Bitboard QueenPseudoAttacks[64];
extern uint8_t BitCount8Bit[256];
////
//// Inline functions
////
/// Functions for testing whether a given bit is set in a bitboard, and for
/// setting and clearing bits.
@@ -123,7 +106,8 @@ inline void do_move_bb(Bitboard *b, Bitboard move_bb) {
*b ^= move_bb;
}
/// rank_bb() and file_bb() take a file or a square as input, and return
/// rank_bb() and file_bb() take a file or a square as input and return
/// a bitboard representing all squares on the given file or rank.
inline Bitboard rank_bb(Rank r) {
@@ -131,7 +115,7 @@ inline Bitboard rank_bb(Rank r) {
}
inline Bitboard rank_bb(Square s) {
return rank_bb(square_rank(s));
return RankBB[square_rank(s)];
}
inline Bitboard file_bb(File f) {
@@ -139,11 +123,11 @@ inline Bitboard file_bb(File f) {
}
inline Bitboard file_bb(Square s) {
return file_bb(square_file(s));
return FileBB[square_file(s)];
}
/// neighboring_files_bb takes a file or a square as input, and returns a
/// neighboring_files_bb takes a file or a square as input and returns a
/// bitboard representing all squares on the neighboring files.
inline Bitboard neighboring_files_bb(File f) {
@@ -155,9 +139,8 @@ inline Bitboard neighboring_files_bb(Square s) {
}
/// this_and_neighboring_files_bb takes a file or a square as input, and
/// returns a bitboard representing all squares on the given and neighboring
/// files.
/// this_and_neighboring_files_bb takes a file or a square as input and returns
/// a bitboard representing all squares on the given and neighboring files.
inline Bitboard this_and_neighboring_files_bb(File f) {
return ThisAndNeighboringFilesBB[f];
@@ -168,17 +151,6 @@ inline Bitboard this_and_neighboring_files_bb(Square s) {
}
/// relative_rank_bb() takes a color and a rank as input, and returns a bitboard
/// representing all squares on the given rank from the given color's point of
/// view. For instance, relative_rank_bb(WHITE, 7) gives all squares on the
/// 7th rank, while relative_rank_bb(BLACK, 7) gives all squares on the 2nd
/// rank.
inline Bitboard relative_rank_bb(Color c, Rank r) {
return RelativeRankBB[c][r];
}
/// in_front_bb() takes a color and a rank or square as input, and returns a
/// bitboard representing all the squares on all ranks in front of the rank
/// (or square), from the given color's point of view. For instance,
@@ -194,19 +166,6 @@ inline Bitboard in_front_bb(Color c, Square s) {
}
/// behind_bb() takes a color and a rank or square as input, and returns a
/// bitboard representing all the squares on all ranks behind of the rank
/// (or square), from the given color's point of view.
inline Bitboard behind_bb(Color c, Rank r) {
return InFrontBB[opposite_color(c)][r];
}
inline Bitboard behind_bb(Color c, Square s) {
return InFrontBB[opposite_color(c)][square_rank(s)];
}
/// Functions for computing sliding attack bitboards. rook_attacks_bb(),
/// bishop_attacks_bb() and queen_attacks_bb() all take a square and a
/// bitboard of occupied squares as input, and return a bitboard representing
@@ -229,17 +188,13 @@ inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] +
(unsigned(int(b) * int(RMult[s]) ^
int(b >> 32) * int(RMult[s] >> 32))
>> RShift[s])];
(unsigned(int(b) * int(RMult[s]) ^ int(b >> 32) * int(RMult[s] >> 32)) >> RShift[s])];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] +
(unsigned(int(b) * int(BMult[s]) ^
int(b >> 32) * int(BMult[s] >> 32))
>> BShift[s])];
(unsigned(int(b) * int(BMult[s]) ^ int(b >> 32) * int(BMult[s] >> 32)) >> BShift[s])];
}
#endif
@@ -269,14 +224,6 @@ inline Bitboard squares_in_front_of(Color c, Square s) {
}
/// squares_behind is similar to squares_in_front, but returns the squares
/// behind the square instead of in front of the square.
inline Bitboard squares_behind(Color c, Square s) {
return SquaresInFrontMask[opposite_color(c)][s];
}
/// passed_pawn_mask takes a color and a square as input, and returns a
/// bitboard mask which can be used to test if a pawn of the given color on
/// the given square is a passed pawn. Definition of the table is:
@@ -310,15 +257,25 @@ inline bool squares_aligned(Square s1, Square s2, Square s3) {
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard.
#if defined(USE_BSFQ) // Assembly code by Heinz van Saanen
#if defined(USE_BSFQ)
inline Square first_1(Bitboard b) {
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
FORCE_INLINE Square first_1(Bitboard b) {
unsigned long index;
_BitScanForward64(&index, b);
return (Square) index;
}
#else
FORCE_INLINE Square first_1(Bitboard b) { // Assembly code by Heinz van Saanen
Bitboard dummy;
__asm__("bsfq %1, %0": "=r"(dummy): "rm"(b) );
return (Square)(dummy);
return (Square) dummy;
}
#endif
inline Square pop_1st_bit(Bitboard* b) {
FORCE_INLINE Square pop_1st_bit(Bitboard* b) {
const Square s = first_1(*b);
*b &= ~(1ULL<<s);
return s;
@@ -332,12 +289,7 @@ extern Square pop_1st_bit(Bitboard* b);
#endif
////
//// Prototypes
////
extern void print_bitboard(Bitboard b);
extern void init_bitboards();
#endif // !defined(BITBOARD_H_INCLUDED)
+3 -30
View File
@@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(BITCOUNT_H_INCLUDED)
#define BITCOUNT_H_INCLUDED
@@ -85,8 +84,10 @@ template<>
inline int count_1s<CNT_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT)
return int(b != 0); // Avoid 'b not used' warning
#elif defined(_MSC_VER) && defined(__INTEL_COMPILER)
return _mm_popcnt_u64(b);
#elif defined(_MSC_VER)
return __popcnt64(b);
return (int)__popcnt64(b);
#elif defined(__GNUC__)
unsigned long ret;
__asm__("popcnt %1, %0" : "=r" (ret) : "r" (b));
@@ -94,32 +95,4 @@ inline int count_1s<CNT_POPCNT>(Bitboard b) {
#endif
}
/// cpu_has_popcnt() detects support for popcnt instruction at runtime
inline bool cpu_has_popcnt() {
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0x00000001);
return (CPUInfo[2] >> 23) & 1;
}
/// CpuHasPOPCNT is a global constant initialized at startup that
/// is set to true if CPU on which application runs supports popcnt
/// hardware instruction. Unless USE_POPCNT is not defined.
#if defined(USE_POPCNT)
const bool CpuHasPOPCNT = cpu_has_popcnt();
#else
const bool CpuHasPOPCNT = false;
#endif
/// CpuIs64Bit is a global constant initialized at compile time that
/// is set to true if CPU on which application runs is a 64 bits.
#if defined(IS_64BIT)
const bool CpuIs64Bit = true;
#else
const bool CpuIs64Bit = false;
#endif
#endif // !defined(BITCOUNT_H_INCLUDED)
+209 -256
View File
@@ -20,31 +20,20 @@
/*
The code in this file is based on the opening book code in PolyGlot
by Fabien Letouzey. PolyGlot is available under the GNU General
by Fabien Letouzey. PolyGlot is available under the GNU General
Public License, and can be downloaded from http://wbec-ridderkerk.nl
*/
////
//// Includes
////
#include <cassert>
#include <iostream>
#include "book.h"
#include "movegen.h"
using namespace std;
////
//// Local definitions
////
namespace {
// Book entry size in bytes
const int EntrySize = 16;
// Random numbers from PolyGlot, used to compute book hash keys
const uint64_t Random64[781] = {
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
@@ -316,248 +305,23 @@ namespace {
const int EnPassantIdx = 772;
const int TurnIdx = 780;
// Local functions
uint64_t book_key(const Position& pos);
uint64_t book_piece_key(Piece p, Square s);
uint64_t book_castle_key(const Position& pos);
uint64_t book_ep_key(const Position& pos);
uint64_t book_color_key(const Position& pos);
}
////
//// Functions
////
// C'tor. Make random number generation less deterministic, for book moves
Book::Book() {
for (int i = abs(get_system_time() % 10000); i > 0; i--)
RKiss.rand<unsigned>();
}
/// Destructor. Be sure file is closed before we leave.
Book::~Book() {
close();
}
/// Book::close() closes the file only if it is open, otherwise
/// we can end up in a little mess due to how std::ifstream works.
void Book::close() {
if (is_open())
ifstream::close();
}
/// Book::open() opens a book file with a given file name
void Book::open(const string& fName) {
// Close old file before opening the new
close();
fileName = fName;
ifstream::open(fileName.c_str(), ifstream::in | ifstream::binary);
// Silently return when asked to open a non-exsistent file
if (!is_open())
return;
// Get the book size in number of entries
seekg(0, ios::end);
bookSize = long(tellg()) / EntrySize;
seekg(0, ios::beg);
if (!good())
{
cerr << "Failed to open book file " << fileName << endl;
exit(EXIT_FAILURE);
}
}
/// Book::file_name() returns the file name of the currently active book,
/// or the empty string if no book is open.
const string Book::file_name() { // Not const to compile on HP-UX 11.X
return is_open() ? fileName : "";
}
/// Book::get_move() gets a book move for a given position. Returns
/// MOVE_NONE if no book move is found.
Move Book::get_move(const Position& pos, bool findBestMove) {
if (!is_open() || bookSize == 0)
return MOVE_NONE;
BookEntry entry;
int bookMove = MOVE_NONE;
unsigned scoresSum = 0, bestScore = 0;
uint64_t key = book_key(pos);
// Choose a book move among the possible moves for the given position
for (int idx = find_key(key); idx < bookSize; idx++)
{
read_entry(entry, idx);
if (entry.key != key)
break;
unsigned score = entry.count;
// If findBestMove is true choose highest rated book move
if (findBestMove)
{
if (score > bestScore)
{
bestScore = score;
bookMove = entry.move;
}
continue;
}
// Choose book move according to its score. If a move has a very
// high score it has more probability to be choosen then a one with
// lower score. Note that first entry is always chosen.
scoresSum += score;
if (RKiss.rand<unsigned>() % scoresSum < score)
bookMove = entry.move;
}
if (!bookMove)
return MOVE_NONE;
// Verify the book move is legal
MoveStack mlist[MOVES_MAX];
MoveStack* last = generate_moves(pos, mlist);
for (MoveStack* cur = mlist; cur != last; cur++)
if ((int(cur->move) & 07777) == bookMove)
return cur->move;
return MOVE_NONE;
}
/// Book::find_key() takes a book key as input, and does a binary search
/// through the book file for the given key. The index to the first book
/// entry with the same key as the input is returned. When the key is not
/// found in the book file, bookSize is returned.
int Book::find_key(uint64_t key) {
int left, right, mid;
BookEntry entry;
// Binary search (finds the leftmost entry)
left = 0;
right = bookSize - 1;
assert(left <= right);
while (left < right)
{
mid = (left + right) / 2;
assert(mid >= left && mid < right);
read_entry(entry, mid);
if (key <= entry.key)
right = mid;
else
left = mid + 1;
}
assert(left == right);
read_entry(entry, left);
return entry.key == key ? left : bookSize;
}
/// Book::read_entry() takes a BookEntry reference and an integer index as
/// input, and looks up the opening book entry at the given index in the book
/// file. The book entry is copied to the first input parameter.
void Book::read_entry(BookEntry& entry, int idx) {
assert(idx >= 0 && idx < bookSize);
assert(is_open());
seekg(idx * EntrySize, ios_base::beg);
*this >> entry;
if (!good())
{
cerr << "Failed to read book entry at index " << idx << endl;
exit(EXIT_FAILURE);
}
}
/// Book::read_integer() reads size chars from the file stream
/// and converts them in an integer number.
uint64_t Book::read_integer(int size) {
char buf[8];
uint64_t n = 0;
read(buf, size);
// Numbers are stored on disk as a binary byte stream
for (int i = 0; i < size; i++)
n = (n << 8) + (unsigned char)buf[i];
return n;
}
////
//// Local definitions
////
namespace {
// book_key() builds up a PolyGlot hash key out of a position
uint64_t book_key(const Position& pos) {
// Piece offset is calculated as (64 * PolyPieceType + square), where
// PolyPieceType is: BP = 0, WP = 1, BN = 2, WN = 3 .... BK = 10, WK = 11
static const int PieceToPoly[] = { 0, 1, 3, 5, 7, 9, 11, 0, 0, 0, 2, 4, 6, 8, 10 };
uint64_t result = 0;
Bitboard b = pos.occupied_squares();
while (b)
{
Square s = pop_1st_bit(&b);
result ^= book_piece_key(pos.piece_on(s), s);
int p = PieceToPoly[int(pos.piece_on(s))];
result ^= Random64[PieceIdx + 64 * p + int(s)];
}
result ^= book_castle_key(pos);
result ^= book_ep_key(pos);
result ^= book_color_key(pos);
return result;
}
uint64_t book_piece_key(Piece p, Square s) {
// Convert pieces to the range 0..11
static const int PieceTo12[] = { 0, 0, 2, 4, 6, 8, 10, 0, 0, 1, 3, 5, 7, 9, 11 };
return Random64[PieceIdx + (PieceTo12[int(p)]^1) * 64 + int(s)];
}
uint64_t book_castle_key(const Position& pos) {
uint64_t result = 0;
if (pos.can_castle_kingside(WHITE))
result ^= Random64[CastleIdx + 0];
@@ -570,18 +334,207 @@ namespace {
if (pos.can_castle_queenside(BLACK))
result ^= Random64[CastleIdx + 3];
if (pos.ep_square() != SQ_NONE)
result ^= Random64[EnPassantIdx + square_file(pos.ep_square())];
if (pos.side_to_move() == WHITE)
result ^= Random64[TurnIdx];
return result;
}
uint64_t book_ep_key(const Position& pos) {
return pos.ep_square() == SQ_NONE ? 0 : Random64[EnPassantIdx + square_file(pos.ep_square())];
}
uint64_t book_color_key(const Position& pos) {
return pos.side_to_move() == WHITE ? Random64[TurnIdx] : 0;
}
}
/// Book c'tor. Make random number generation less deterministic, for book moves
Book::Book() {
for (int i = abs(get_system_time() % 10000); i > 0; i--)
RKiss.rand<unsigned>();
}
/// Book destructor. Be sure file is closed before we leave.
Book::~Book() {
close();
}
/// Book::close() closes the file only if it is open, otherwise
/// we can end up in a little mess due to how std::ifstream works.
void Book::close() {
if (bookFile.is_open())
bookFile.close();
bookName = "";
}
/// Book::open() opens a book file with a given file name
void Book::open(const string& fileName) {
// Close old file before opening the new
close();
bookFile.open(fileName.c_str(), ifstream::in | ifstream::binary);
// Silently return when asked to open a non-exsistent file
if (!bookFile.is_open())
return;
// Get the book size in number of entries
bookFile.seekg(0, ios::end);
bookSize = long(bookFile.tellg()) / sizeof(BookEntry);
if (!bookFile.good())
{
cerr << "Failed to open book file " << fileName << endl;
exit(EXIT_FAILURE);
}
// Set only if successful
bookName = fileName;
}
/// Book::get_move() gets a book move for a given position. Returns
/// MOVE_NONE if no book move is found. If findBestMove is true then
/// return always the highest rated book move.
Move Book::get_move(const Position& pos, bool findBestMove) {
if (!bookFile.is_open() || bookSize == 0)
return MOVE_NONE;
BookEntry entry;
int bookMove = MOVE_NONE;
unsigned score, scoresSum = 0, bestScore = 0;
uint64_t key = book_key(pos);
// Choose a book move among the possible moves for the given position
for (int idx = find_entry(key); idx < bookSize; idx++)
{
entry = read_entry(idx);
if (entry.key != key)
break;
score = entry.count;
if (!findBestMove)
{
// Choose book move according to its score. If a move has a very
// high score it has more probability to be choosen then a one with
// lower score. Note that first entry is always chosen.
scoresSum += score;
if (RKiss.rand<unsigned>() % scoresSum < score)
bookMove = entry.move;
}
else if (score > bestScore)
{
bestScore = score;
bookMove = entry.move;
}
}
// A PolyGlot book move is encoded as follows:
//
// bit 0- 5: destination square (from 0 to 63)
// bit 6-11: origin square (from 0 to 63)
// bit 12-13-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
//
// Castling moves follow "king captures rook" representation. So in case
// book move is a promotion we have to convert to our representation, in
// all other cases we can directly compare with a Move after having
// masked out special Move's flags that are not supported by PolyGlot.
int p = (bookMove >> 12) & 7;
if (p)
bookMove = int(make_promotion_move(move_from(Move(bookMove)),
move_to(Move(bookMove)), PieceType(p + 1)));
// Verify the book move (if any) is legal
MoveStack mlist[MAX_MOVES];
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
for (MoveStack* cur = mlist; cur != last; cur++)
if ((int(cur->move) & ~(3 << 14)) == bookMove) // Mask out special flags
return cur->move;
return MOVE_NONE;
}
/// Book::find_entry() takes a book key as input, and does a binary search
/// through the book file for the given key. The index to the first book
/// entry with the same key as the input is returned. When the key is not
/// found in the book file, bookSize is returned.
int Book::find_entry(uint64_t key) {
int left, right, mid;
// Binary search (finds the leftmost entry)
left = 0;
right = bookSize - 1;
assert(left <= right);
while (left < right)
{
mid = (left + right) / 2;
assert(mid >= left && mid < right);
if (key <= read_entry(mid).key)
right = mid;
else
left = mid + 1;
}
assert(left == right);
return read_entry(left).key == key ? left : bookSize;
}
/// Book::get_number() reads sizeof(T) chars from the file's binary byte
/// stream and converts them in a number of type T.
template<typename T>
void Book::get_number(T& n) {
n = 0;
for (size_t i = 0; i < sizeof(T); i++)
n = (n << 8) + (T)bookFile.get();
}
/// Book::read_entry() takes an integer index, and returns the BookEntry
/// at the given index in the book file.
BookEntry Book::read_entry(int idx) {
assert(idx >= 0 && idx < bookSize);
assert(bookFile.is_open());
BookEntry e;
bookFile.seekg(idx * sizeof(BookEntry), ios_base::beg);
get_number(e.key);
get_number(e.move);
get_number(e.count);
get_number(e.learn);
if (!bookFile.good())
{
cerr << "Failed to read book entry at index " << idx << endl;
exit(EXIT_FAILURE);
}
return e;
}
+12 -31
View File
@@ -17,22 +17,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
The code in this file is based on the opening book code in PolyGlot
by Fabien Letouzey. PolyGlot is available under the GNU General
Public License, and can be downloaded from http://wbec-ridderkerk.nl
*/
#if !defined(BOOK_H_INCLUDED)
#define BOOK_H_INCLUDED
////
//// Includes
////
#include <fstream>
#include <string>
@@ -41,39 +28,33 @@
#include "rkiss.h"
////
//// Types
////
// A Polyglot book is a series of "entries" of 16 bytes. All integers are
// stored highest byte first (regardless of size). The entries are ordered
// according to key. Lowest key first.
struct BookEntry {
uint64_t key;
uint16_t move;
uint16_t count;
uint16_t n;
uint16_t sum;
uint32_t learn;
};
class Book : private std::ifstream {
Book(const Book&); // just decleared..
Book& operator=(const Book&); // ..to avoid a warning
class Book {
public:
Book();
~Book();
void open(const std::string& fName);
void open(const std::string& fileName);
void close();
const std::string file_name();
Move get_move(const Position& pos, bool findBestMove);
const std::string name() const { return bookName; }
private:
Book& operator>>(uint64_t& n) { n = read_integer(8); return *this; }
Book& operator>>(uint16_t& n) { n = (uint16_t)read_integer(2); return *this; }
void operator>>(BookEntry& e) { *this >> e.key >> e.move >> e.count >> e.n >> e.sum; }
template<typename T> void get_number(T& n);
uint64_t read_integer(int size);
void read_entry(BookEntry& e, int n);
int find_key(uint64_t key);
BookEntry read_entry(int idx);
int find_entry(uint64_t key);
std::string fileName;
std::ifstream bookFile;
std::string bookName;
int bookSize;
RKISS RKiss;
};
-56
View File
@@ -1,56 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(COLOR_H_INCLUDED)
#define COLOR_H_INCLUDED
#include "types.h"
////
//// Types
////
enum Color {
WHITE,
BLACK,
COLOR_NONE
};
enum SquareColor {
DARK,
LIGHT
};
ENABLE_OPERATORS_ON(Color);
////
//// Inline functions
////
inline Color opposite_color(Color c) {
return Color(int(c) ^ 1);
}
inline bool color_is_ok(Color c) {
return c == WHITE || c == BLACK;
}
#endif // !defined(COLOR_H_INCLUDED)
-43
View File
@@ -1,43 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(DEPTH_H_INCLUDED)
#define DEPTH_H_INCLUDED
#include "types.h"
////
//// Types
////
enum Depth {
ONE_PLY = 2,
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY
};
ENABLE_OPERATORS_ON(Depth);
#endif // !defined(DEPTH_H_INCLUDED)
+126 -97
View File
@@ -17,26 +17,21 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include "bitcount.h"
#include "endgame.h"
#include "pawns.h"
using std::string;
////
//// Local definitions
////
extern uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm);
namespace {
// Table used to drive the defending king towards the edge of the board
// in KX vs K and KQ vs KR endgames.
const uint8_t MateTable[64] = {
const int MateTable[64] = {
100, 90, 80, 70, 70, 80, 90, 100,
90, 70, 60, 50, 50, 60, 70, 90,
80, 60, 40, 30, 30, 40, 60, 80,
@@ -49,7 +44,7 @@ namespace {
// Table used to drive the defending king towards a corner square of the
// right color in KBN vs K endgames.
const uint8_t KBNKMateTable[64] = {
const int KBNKMateTable[64] = {
200, 190, 180, 170, 160, 150, 140, 130,
190, 180, 170, 160, 150, 140, 130, 140,
180, 170, 155, 140, 140, 125, 140, 150,
@@ -68,52 +63,103 @@ namespace {
// and knight in KR vs KN endgames.
const int KRKNKingKnightDistancePenalty[8] = { 0, 0, 4, 10, 20, 32, 48, 70 };
// Bitbase for KP vs K
uint8_t KPKBitbase[24576];
// Build corresponding key code for the opposite color: "KBPKN" -> "KNKBP"
const string swap_colors(const string& keyCode) {
// Various inline functions for accessing the above arrays
inline Value mate_table(Square s) {
return Value(MateTable[s]);
size_t idx = keyCode.find('K', 1);
return keyCode.substr(idx) + keyCode.substr(0, idx);
}
inline Value kbnk_mate_table(Square s) {
return Value(KBNKMateTable[s]);
// Get the material key of a position out of the given endgame key code
// like "KBPKN". The trick here is to first build up a FEN string and then
// let a Position object to do the work for us. Note that the FEN string
// could correspond to an illegal position.
Key mat_key(const string& keyCode) {
assert(keyCode.length() > 0 && keyCode.length() < 8);
assert(keyCode[0] == 'K');
string fen;
size_t i = 0;
// First add white and then black pieces
do fen += keyCode[i]; while (keyCode[++i] != 'K');
do fen += char(tolower(keyCode[i])); while (++i < keyCode.length());
// Add file padding and remaining empty ranks
fen += string(1, '0' + int(8 - keyCode.length())) + "/8/8/8/8/8/8/8 w - -";
// Build a Position out of the fen string and get its material key
return Position(fen, false, 0).get_material_key();
}
inline Value distance_bonus(int d) {
return Value(DistanceBonus[d]);
}
typedef EndgameBase<Value> EF;
typedef EndgameBase<ScaleFactor> SF;
inline Value krkn_king_knight_distance_penalty(int d) {
return Value(KRKNKingKnightDistancePenalty[d]);
}
} // namespace
// Function for probing the KP vs K bitbase
int probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm);
/// Endgames member definitions
template<> const Endgames::EFMap& Endgames::get<EF>() const { return maps.first; }
template<> const Endgames::SFMap& Endgames::get<SF>() const { return maps.second; }
Endgames::Endgames() {
add<Endgame<Value, KNNK> >("KNNK");
add<Endgame<Value, KPK> >("KPK");
add<Endgame<Value, KBNK> >("KBNK");
add<Endgame<Value, KRKP> >("KRKP");
add<Endgame<Value, KRKB> >("KRKB");
add<Endgame<Value, KRKN> >("KRKN");
add<Endgame<Value, KQKR> >("KQKR");
add<Endgame<Value, KBBKN> >("KBBKN");
add<Endgame<ScaleFactor, KNPK> >("KNPK");
add<Endgame<ScaleFactor, KRPKR> >("KRPKR");
add<Endgame<ScaleFactor, KBPKB> >("KBPKB");
add<Endgame<ScaleFactor, KBPPKB> >("KBPPKB");
add<Endgame<ScaleFactor, KBPKN> >("KBPKN");
add<Endgame<ScaleFactor, KRPPKRP> >("KRPPKRP");
}
Endgames::~Endgames() {
////
//// Functions
////
for (EFMap::const_iterator it = get<EF>().begin(); it != get<EF>().end(); ++it)
delete it->second;
/// init_bitbases() is called during program initialization, and simply loads
/// bitbases from disk into memory. At the moment, there is only the bitbase
/// for KP vs K, but we may decide to add other bitbases later.
extern void generate_kpk_bitbase(uint8_t bitbase[]);
void init_bitbases() {
generate_kpk_bitbase(KPKBitbase);
for (SFMap::const_iterator it = get<SF>().begin(); it != get<SF>().end(); ++it)
delete it->second;
}
template<class T>
void Endgames::add(const string& keyCode) {
typedef typename T::Base F;
typedef std::map<Key, F*> M;
const_cast<M&>(get<F>()).insert(std::pair<Key, F*>(mat_key(keyCode), new T(WHITE)));
const_cast<M&>(get<F>()).insert(std::pair<Key, F*>(mat_key(swap_colors(keyCode)), new T(BLACK)));
}
template<class T>
T* Endgames::get(Key key) const {
typename std::map<Key, T*>::const_iterator it = get<T>().find(key);
return it != get<T>().end() ? it->second : NULL;
}
// Explicit template instantiations
template EF* Endgames::get<EF>(Key key) const;
template SF* Endgames::get<SF>(Key key) const;
/// Mate with KX vs K. This function is used to evaluate positions with
/// King and plenty of material vs a lone king. It simply gives the
/// attacking side a bonus for driving the defending king towards the edge
/// of the board, and for keeping the distance between the two kings small.
template<>
Value EvaluationFunction<KXK>::apply(const Position& pos) const {
Value Endgame<Value, KXK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
@@ -123,8 +169,8 @@ Value EvaluationFunction<KXK>::apply(const Position& pos) const {
Value result = pos.non_pawn_material(strongerSide)
+ pos.piece_count(strongerSide, PAWN) * PawnValueEndgame
+ mate_table(loserKSq)
+ distance_bonus(square_distance(winnerKSq, loserKSq));
+ MateTable[loserKSq]
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
if ( pos.piece_count(strongerSide, QUEEN)
|| pos.piece_count(strongerSide, ROOK)
@@ -139,7 +185,7 @@ Value EvaluationFunction<KXK>::apply(const Position& pos) const {
/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
/// defending king towards a corner square of the right color.
template<>
Value EvaluationFunction<KBNK>::apply(const Position& pos) const {
Value Endgame<Value, KBNK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
@@ -155,15 +201,15 @@ Value EvaluationFunction<KBNK>::apply(const Position& pos) const {
// kbnk_mate_table() tries to drive toward corners A1 or H8,
// if we have a bishop that cannot reach the above squares we
// mirror the kings so to drive enemy toward corners A8 or H1.
if (!same_color_squares(bishopSquare, SQ_A1))
if (opposite_color_squares(bishopSquare, SQ_A1))
{
winnerKSq = flop_square(winnerKSq);
loserKSq = flop_square(loserKSq);
}
Value result = VALUE_KNOWN_WIN
+ distance_bonus(square_distance(winnerKSq, loserKSq))
+ kbnk_mate_table(loserKSq);
+ DistanceBonus[square_distance(winnerKSq, loserKSq)]
+ KBNKMateTable[loserKSq];
return strongerSide == pos.side_to_move() ? result : -result;
}
@@ -171,7 +217,7 @@ Value EvaluationFunction<KBNK>::apply(const Position& pos) const {
/// KP vs K. This endgame is evaluated with the help of a bitbase.
template<>
Value EvaluationFunction<KPK>::apply(const Position& pos) const {
Value Endgame<Value, KPK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
@@ -203,7 +249,7 @@ Value EvaluationFunction<KPK>::apply(const Position& pos) const {
wpsq = flop_square(wpsq);
}
if (!probe_kpk(wksq, wpsq, bksq, stm))
if (!probe_kpk_bitbase(wksq, wpsq, bksq, stm))
return VALUE_DRAW;
Value result = VALUE_KNOWN_WIN
@@ -219,7 +265,7 @@ Value EvaluationFunction<KPK>::apply(const Position& pos) const {
/// far advanced with support of the king, while the attacking king is far
/// away.
template<>
Value EvaluationFunction<KRKP>::apply(const Position& pos) const {
Value Endgame<Value, KRKP>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -276,7 +322,7 @@ Value EvaluationFunction<KRKP>::apply(const Position& pos) const {
/// KR vs KB. This is very simple, and always returns drawish scores. The
/// score is slightly bigger when the defending king is close to the edge.
template<>
Value EvaluationFunction<KRKB>::apply(const Position& pos) const {
Value Endgame<Value, KRKB>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -284,7 +330,7 @@ Value EvaluationFunction<KRKB>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.piece_count(weakerSide, BISHOP) == 1);
Value result = mate_table(pos.king_square(weakerSide));
Value result = Value(MateTable[pos.king_square(weakerSide)]);
return strongerSide == pos.side_to_move() ? result : -result;
}
@@ -292,7 +338,7 @@ Value EvaluationFunction<KRKB>::apply(const Position& pos) const {
/// KR vs KN. The attacking side has slightly better winning chances than
/// in KR vs KB, particularly if the king and the knight are far apart.
template<>
Value EvaluationFunction<KRKN>::apply(const Position& pos) const {
Value Endgame<Value, KRKN>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -305,8 +351,8 @@ Value EvaluationFunction<KRKN>::apply(const Position& pos) const {
int d = square_distance(defendingKSq, nSq);
Value result = Value(10)
+ mate_table(defendingKSq)
+ krkn_king_knight_distance_penalty(d);
+ MateTable[defendingKSq]
+ KRKNKingKnightDistancePenalty[d];
return strongerSide == pos.side_to_move() ? result : -result;
}
@@ -318,7 +364,7 @@ Value EvaluationFunction<KRKN>::apply(const Position& pos) const {
/// for the defending side in the search, this is usually sufficient to be
/// able to win KQ vs KR.
template<>
Value EvaluationFunction<KQKR>::apply(const Position& pos) const {
Value Endgame<Value, KQKR>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -330,14 +376,14 @@ Value EvaluationFunction<KQKR>::apply(const Position& pos) const {
Value result = QueenValueEndgame
- RookValueEndgame
+ mate_table(loserKSq)
+ distance_bonus(square_distance(winnerKSq, loserKSq));
+ MateTable[loserKSq]
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
return strongerSide == pos.side_to_move() ? result : -result;
}
template<>
Value EvaluationFunction<KBBKN>::apply(const Position& pos) const {
Value Endgame<Value, KBBKN>::apply(const Position& pos) const {
assert(pos.piece_count(strongerSide, BISHOP) == 2);
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame);
@@ -351,7 +397,7 @@ Value EvaluationFunction<KBBKN>::apply(const Position& pos) const {
Square nsq = pos.piece_list(weakerSide, KNIGHT, 0);
// Bonus for attacking king close to defending king
result += distance_bonus(square_distance(wksq, bksq));
result += Value(DistanceBonus[square_distance(wksq, bksq)]);
// Bonus for driving the defending king and knight apart
result += Value(square_distance(bksq, nsq) * 32);
@@ -366,12 +412,12 @@ Value EvaluationFunction<KBBKN>::apply(const Position& pos) const {
/// K and two minors vs K and one or two minors or K and two knights against
/// king alone are always draw.
template<>
Value EvaluationFunction<KmmKm>::apply(const Position&) const {
Value Endgame<Value, KmmKm>::apply(const Position&) const {
return VALUE_DRAW;
}
template<>
Value EvaluationFunction<KNNK>::apply(const Position&) const {
Value Endgame<Value, KNNK>::apply(const Position&) const {
return VALUE_DRAW;
}
@@ -381,7 +427,7 @@ Value EvaluationFunction<KNNK>::apply(const Position&) const {
/// returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// will be used.
template<>
ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) const {
ScaleFactor Endgame<ScaleFactor, KBPsK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -401,8 +447,8 @@ ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) const {
Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8));
Square kingSq = pos.king_square(weakerSide);
if ( !same_color_squares(queeningSq, bishopSq)
&& file_distance(square_file(kingSq), pawnFile) <= 1)
if ( opposite_color_squares(queeningSq, bishopSq)
&& abs(square_file(kingSq) - pawnFile) <= 1)
{
// The bishop has the wrong color, and the defending king is on the
// file of the pawn(s) or the neighboring file. Find the rank of the
@@ -435,7 +481,7 @@ ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) const {
/// It tests for fortress draws with a rook on the third rank defended by
/// a pawn.
template<>
ScaleFactor ScalingFunction<KQKRPs>::apply(const Position& pos) const {
ScaleFactor Endgame<ScaleFactor, KQKRPs>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.piece_count(strongerSide, QUEEN) == 1);
@@ -446,8 +492,8 @@ ScaleFactor ScalingFunction<KQKRPs>::apply(const Position& pos) const {
Square kingSq = pos.king_square(weakerSide);
if ( relative_rank(weakerSide, kingSq) <= RANK_2
&& relative_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4
&& (pos.pieces(ROOK, weakerSide) & relative_rank_bb(weakerSide, RANK_3))
&& (pos.pieces(PAWN, weakerSide) & relative_rank_bb(weakerSide, RANK_2))
&& (pos.pieces(ROOK, weakerSide) & rank_bb(relative_rank(weakerSide, RANK_3)))
&& (pos.pieces(PAWN, weakerSide) & rank_bb(relative_rank(weakerSide, RANK_2)))
&& (pos.attacks_from<KING>(kingSq) & pos.pieces(PAWN, weakerSide)))
{
Square rsq = pos.piece_list(weakerSide, ROOK, 0);
@@ -466,7 +512,7 @@ ScaleFactor ScalingFunction<KQKRPs>::apply(const Position& pos) const {
/// It would also be nice to rewrite the actual code for this function,
/// which is mostly copied from Glaurung 1.x, and not very pretty.
template<>
ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 1);
@@ -584,7 +630,7 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
/// single pattern: If the stronger side has no pawns and the defending king
/// is actively placed, the position is drawish.
template<>
ScaleFactor ScalingFunction<KRPPKRP>::apply(const Position& pos) const {
ScaleFactor Endgame<ScaleFactor, KRPPKRP>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 2);
@@ -623,7 +669,7 @@ ScaleFactor ScalingFunction<KRPPKRP>::apply(const Position& pos) const {
/// against king. There is just a single rule here: If all pawns are on
/// the same rook file and are blocked by the defending king, it's a draw.
template<>
ScaleFactor ScalingFunction<KPsK>::apply(const Position& pos) const {
ScaleFactor Endgame<ScaleFactor, KPsK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) >= 2);
@@ -661,7 +707,7 @@ ScaleFactor ScalingFunction<KPsK>::apply(const Position& pos) const {
/// it's a draw. If the two bishops have opposite color, it's almost always
/// a draw.
template<>
ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const {
ScaleFactor Endgame<ScaleFactor, KBPKB>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -678,12 +724,12 @@ ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const {
// Case 1: Defending king blocks the pawn, and cannot be driven away
if ( square_file(weakerKingSq) == square_file(pawnSq)
&& relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
&& ( !same_color_squares(weakerKingSq, strongerBishopSq)
&& ( opposite_color_squares(weakerKingSq, strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return SCALE_FACTOR_ZERO;
// Case 2: Opposite colored bishops
if (!same_color_squares(strongerBishopSq, weakerBishopSq))
if (opposite_color_squares(strongerBishopSq, weakerBishopSq))
{
// We assume that the position is drawn in the following three situations:
//
@@ -716,7 +762,7 @@ ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const {
/// KBPPKBScalingFunction scales KBPP vs KB endgames. It detects a few basic
/// draws with opposite-colored bishops.
template<>
ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -728,8 +774,7 @@ ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
Square wbsq = pos.piece_list(strongerSide, BISHOP, 0);
Square bbsq = pos.piece_list(weakerSide, BISHOP, 0);
if (same_color_squares(wbsq, bbsq))
// Not opposite-colored bishops, no scaling
if (!opposite_color_squares(wbsq, bbsq))
return SCALE_FACTOR_NONE;
Square ksq = pos.king_square(weakerSide);
@@ -757,7 +802,7 @@ ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
// some square in the frontmost pawn's path.
if ( square_file(ksq) == square_file(blockSq1)
&& relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1)
&& !same_color_squares(ksq, wbsq))
&& opposite_color_squares(ksq, wbsq))
return SCALE_FACTOR_ZERO;
else
return SCALE_FACTOR_NONE;
@@ -767,14 +812,14 @@ ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
// in front of the frontmost pawn's path, and the square diagonally behind
// this square on the file of the other pawn.
if ( ksq == blockSq1
&& !same_color_squares(ksq, wbsq)
&& opposite_color_squares(ksq, wbsq)
&& ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(BISHOP, weakerSide))
|| rank_distance(r1, r2) >= 2))
|| abs(r1 - r2) >= 2))
return SCALE_FACTOR_ZERO;
else if ( ksq == blockSq2
&& !same_color_squares(ksq, wbsq)
&& opposite_color_squares(ksq, wbsq)
&& ( bbsq == blockSq1
|| (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(BISHOP, weakerSide))))
return SCALE_FACTOR_ZERO;
@@ -793,7 +838,7 @@ ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
/// square of the king is not of the same color as the stronger side's bishop,
/// it's a draw.
template<>
ScaleFactor ScalingFunction<KBPKN>::apply(const Position& pos) const {
ScaleFactor Endgame<ScaleFactor, KBPKN>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -808,7 +853,7 @@ ScaleFactor ScalingFunction<KBPKN>::apply(const Position& pos) const {
if ( square_file(weakerKingSq) == square_file(pawnSq)
&& relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
&& ( !same_color_squares(weakerKingSq, strongerBishopSq)
&& ( opposite_color_squares(weakerKingSq, strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return SCALE_FACTOR_ZERO;
@@ -820,7 +865,7 @@ ScaleFactor ScalingFunction<KBPKN>::apply(const Position& pos) const {
/// If the pawn is a rook pawn on the 7th rank and the defending king prevents
/// the pawn from advancing, the position is drawn.
template<>
ScaleFactor ScalingFunction<KNPK>::apply(const Position& pos) const {
ScaleFactor Endgame<ScaleFactor, KNPK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame);
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
@@ -850,7 +895,7 @@ ScaleFactor ScalingFunction<KNPK>::apply(const Position& pos) const {
/// advanced and not on a rook file; in this case it is often possible to win
/// (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
template<>
ScaleFactor ScalingFunction<KPKP>::apply(const Position& pos) const {
ScaleFactor Endgame<ScaleFactor, KPKP>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
@@ -890,21 +935,5 @@ ScaleFactor ScalingFunction<KPKP>::apply(const Position& pos) const {
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a
// draw, it's probably at least a draw even with the pawn.
return probe_kpk(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_ZERO;
}
namespace {
// Probe the KP vs K bitbase
int probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) {
int wp = square_file(wpsq) + 4 * (square_rank(wpsq) - 1);
int index = int(stm) + 2 * bksq + 128 * wksq + 8192 * wp;
assert(index >= 0 && index < 24576 * 8);
return KPKBitbase[index / 8] & (1 << (index & 7));
}
return probe_kpk_bitbase(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_ZERO;
}
+68 -60
View File
@@ -17,88 +17,96 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(ENDGAME_H_INCLUDED)
#define ENDGAME_H_INCLUDED
////
//// Includes
////
#include <string>
#include <map>
#include "position.h"
#include "value.h"
#include "types.h"
////
//// Types
////
/// EndgameType lists all supported endgames
enum EndgameType {
// Evaluation functions
KXK, // Generic "mate lone king" eval
KBNK, // KBN vs K
KPK, // KP vs K
KRKP, // KR vs KP
KRKB, // KR vs KB
KRKN, // KR vs KN
KQKR, // KQ vs KR
KBBKN, // KBB vs KN
KNNK, // KNN vs K
KmmKm, // K and two minors vs K and one or two minors
// Evaluation functions
// Scaling functions
KBPsK, // KB+pawns vs K
KQKRPs, // KQ vs KR+pawns
KRPKR, // KRP vs KR
KRPPKRP, // KRPP vs KRP
KPsK, // King and pawns vs king
KBPKB, // KBP vs KB
KBPPKB, // KBPP vs KB
KBPKN, // KBP vs KN
KNPK, // KNP vs K
KPKP // KP vs KP
KXK, // Generic "mate lone king" eval
KBNK, // KBN vs K
KPK, // KP vs K
KRKP, // KR vs KP
KRKB, // KR vs KB
KRKN, // KR vs KN
KQKR, // KQ vs KR
KBBKN, // KBB vs KN
KNNK, // KNN vs K
KmmKm, // K and two minors vs K and one or two minors
// Scaling functions
KBPsK, // KB+pawns vs K
KQKRPs, // KQ vs KR+pawns
KRPKR, // KRP vs KR
KRPPKRP, // KRPP vs KRP
KPsK, // King and pawns vs king
KBPKB, // KBP vs KB
KBPPKB, // KBPP vs KB
KBPKN, // KBP vs KN
KNPK, // KNP vs K
KPKP // KP vs KP
};
/// Template abstract base class for all special endgame functions
/// Base and derived templates for endgame evaluation and scaling functions
template<typename T>
class EndgameFunctionBase {
public:
EndgameFunctionBase(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {}
virtual ~EndgameFunctionBase() {}
virtual T apply(const Position&) const = 0;
Color color() const { return strongerSide; }
struct EndgameBase {
protected:
typedef EndgameBase<T> Base;
virtual ~EndgameBase() {}
virtual Color color() const = 0;
virtual T apply(const Position&) const = 0;
};
template<typename T, EndgameType>
struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {}
Color color() const { return strongerSide; }
T apply(const Position&) const;
private:
Color strongerSide, weakerSide;
};
typedef EndgameFunctionBase<Value> EndgameEvaluationFunctionBase;
typedef EndgameFunctionBase<ScaleFactor> EndgameScalingFunctionBase;
/// Endgames class stores in two std::map the pointers to endgame evaluation
/// and scaling base objects. Then we use polymorphism to invoke the actual
/// endgame function calling its apply() method that is virtual.
/// Templates subclass for various concrete endgames
class Endgames {
template<EndgameType>
struct EvaluationFunction : public EndgameEvaluationFunctionBase {
typedef EndgameEvaluationFunctionBase Base;
explicit EvaluationFunction(Color c): EndgameEvaluationFunctionBase(c) {}
Value apply(const Position&) const;
typedef std::map<Key, EndgameBase<Value>* > EFMap;
typedef std::map<Key, EndgameBase<ScaleFactor>* > SFMap;
public:
Endgames();
~Endgames();
template<class T> T* get(Key key) const;
private:
template<class T> void add(const std::string& keyCode);
// Here we store two maps, for evaluate and scaling functions...
std::pair<EFMap, SFMap> maps;
// ...and here is the accessing template function
template<typename T> const std::map<Key, T*>& get() const;
};
template<EndgameType>
struct ScalingFunction : public EndgameScalingFunctionBase {
typedef EndgameScalingFunctionBase Base;
explicit ScalingFunction(Color c) : EndgameScalingFunctionBase(c) {}
ScaleFactor apply(const Position&) const;
};
////
//// Prototypes
////
extern void init_bitbases();
#endif // !defined(ENDGAME_H_INCLUDED)
+333 -236
View File
@@ -17,12 +17,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include <iostream>
#include <iomanip>
#include <sstream>
#include "bitcount.h"
#include "evaluate.h"
@@ -31,18 +29,14 @@
#include "thread.h"
#include "ucioption.h"
////
//// Local definitions
////
namespace {
// Struct EvalInfo contains various information computed and collected
// by the evaluation functions.
struct EvalInfo {
// Pointer to pawn hash table entry
// Pointers to material and pawn hash table entries
MaterialInfo* mi;
PawnInfo* pi;
// attackedBy[color][piece type] is a bitboard representing all squares
@@ -81,7 +75,7 @@ namespace {
const int GrainSize = 8;
// Evaluation weights, initialized from UCI options
enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem };
enum { Mobility, PassedPawns, Space, KingDangerUs, KingDangerThem };
Score Weights[6];
typedef Value V;
@@ -94,7 +88,7 @@ namespace {
//
// Values modified by Joona Kiiski
const Score WeightsInternal[] = {
S(248, 271), S(233, 201), S(252, 259), S(46, 0), S(247, 0), S(259, 0)
S(248, 271), S(252, 259), S(46, 0), S(247, 0), S(259, 0)
};
// MobilityBonus[PieceType][attacked] contains mobility bonuses for middle and
@@ -148,9 +142,9 @@ namespace {
{ S(0, 0), S(15, 39), S(15, 39), S(15, 39), S(15, 39), S( 0, 0) } // QUEEN
};
// ThreatedByPawnPenalty[PieceType] contains a penalty according to which
// ThreatenedByPawnPenalty[PieceType] contains a penalty according to which
// piece type is attacked by an enemy pawn.
const Score ThreatedByPawnPenalty[] = {
const Score ThreatenedByPawnPenalty[] = {
S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118)
};
@@ -168,6 +162,11 @@ namespace {
// right to castle.
const Value TrappedRookPenalty = Value(180);
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
// a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
// happen in Chess960 games.
const Score TrappedBishopA1H1Penalty = make_score(100, 100);
// The SpaceMask[Color] contains the area of the board which is considered
// by the space evaluation. In the middle game, each side is given a bonus
// based on how many squares inside this area are safe and available for
@@ -214,22 +213,27 @@ namespace {
// weighted scores, indexed by color and by a calculated integer number.
Score KingDangerTable[2][128];
// Pawn and material hash tables, indexed by the current thread id.
// Note that they will be initialized at 0 being global variables.
MaterialInfoTable* MaterialTable[MAX_THREADS];
PawnInfoTable* PawnTable[MAX_THREADS];
// TracedTerms[Color][PieceType || TracedType] contains a breakdown of the
// evaluation terms, used when tracing.
Score TracedScores[2][16];
std::stringstream TraceStream;
enum TracedType {
PST = 8, IMBALANCE = 9, MOBILITY = 10, THREAT = 11,
PASSED = 12, UNSTOPPABLE = 13, SPACE = 14, TOTAL = 15
};
// Function prototypes
template<bool HasPopCnt>
template<bool HasPopCnt, bool Trace>
Value do_evaluate(const Position& pos, Value& margin);
template<Color Us, bool HasPopCnt>
void init_eval_info(const Position& pos, EvalInfo& ei);
template<Color Us, bool HasPopCnt>
template<Color Us, bool HasPopCnt, bool Trace>
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility);
template<Color Us, bool HasPopCnt>
template<Color Us, bool HasPopCnt, bool Trace>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]);
template<Color Us>
@@ -248,19 +252,8 @@ namespace {
Value scale_by_game_phase(const Score& v, Phase ph, ScaleFactor sf);
Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight);
void init_safety();
}
////
//// Functions
////
/// Prefetches in pawn hash tables
void prefetchPawn(Key key, int threadID) {
PawnTable[threadID]->prefetch(key);
double to_cp(Value v);
void trace_add(int idx, Score term_w, Score term_b = SCORE_ZERO);
}
@@ -269,89 +262,88 @@ void prefetchPawn(Key key, int threadID) {
/// between them based on the remaining material.
Value evaluate(const Position& pos, Value& margin) {
return CpuHasPOPCNT ? do_evaluate<true>(pos, margin)
: do_evaluate<false>(pos, margin);
return CpuHasPOPCNT ? do_evaluate<true, false>(pos, margin)
: do_evaluate<false, false>(pos, margin);
}
namespace {
template<bool HasPopCnt>
template<bool HasPopCnt, bool Trace>
Value do_evaluate(const Position& pos, Value& margin) {
EvalInfo ei;
Value margins[2];
Score mobilityWhite, mobilityBlack;
Score score, mobilityWhite, mobilityBlack;
assert(pos.is_ok());
assert(pos.thread() >= 0 && pos.thread() < MAX_THREADS);
assert(!pos.is_check());
assert(!pos.in_check());
// Initialize value by reading the incrementally updated scores included
// Initialize score by reading the incrementally updated scores included
// in the position object (material + piece square tables).
Score bonus = pos.value();
score = pos.value();
// margins[] store the uncertainty estimation of position's evaluation
// that typically is used by the search for pruning decisions.
margins[WHITE] = margins[BLACK] = VALUE_ZERO;
// Probe the material hash table
MaterialInfo* mi = MaterialTable[pos.thread()]->get_material_info(pos);
bonus += mi->material_value();
ei.mi = Threads[pos.thread()].materialTable.get_material_info(pos);
score += ei.mi->material_value();
// If we have a specialized evaluation function for the current material
// configuration, call it and return.
if (mi->specialized_eval_exists())
if (ei.mi->specialized_eval_exists())
{
margin = VALUE_ZERO;
return mi->evaluate(pos);
return ei.mi->evaluate(pos);
}
// Probe the pawn hash table
ei.pi = PawnTable[pos.thread()]->get_pawn_info(pos);
bonus += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]);
ei.pi = Threads[pos.thread()].pawnTable.get_pawn_info(pos);
score += ei.pi->pawns_value();
// Initialize attack and king safety bitboards
init_eval_info<WHITE, HasPopCnt>(pos, ei);
init_eval_info<BLACK, HasPopCnt>(pos, ei);
// Evaluate pieces and mobility
bonus += evaluate_pieces_of_color<WHITE, HasPopCnt>(pos, ei, mobilityWhite)
- evaluate_pieces_of_color<BLACK, HasPopCnt>(pos, ei, mobilityBlack);
score += evaluate_pieces_of_color<WHITE, HasPopCnt, Trace>(pos, ei, mobilityWhite)
- evaluate_pieces_of_color<BLACK, HasPopCnt, Trace>(pos, ei, mobilityBlack);
bonus += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]);
score += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]);
// Evaluate kings after all other pieces because we need complete attack
// information when computing the king safety evaluation.
bonus += evaluate_king<WHITE, HasPopCnt>(pos, ei, margins)
- evaluate_king<BLACK, HasPopCnt>(pos, ei, margins);
score += evaluate_king<WHITE, HasPopCnt, Trace>(pos, ei, margins)
- evaluate_king<BLACK, HasPopCnt, Trace>(pos, ei, margins);
// Evaluate tactical threats, we need full attack information including king
bonus += evaluate_threats<WHITE>(pos, ei)
score += evaluate_threats<WHITE>(pos, ei)
- evaluate_threats<BLACK>(pos, ei);
// Evaluate passed pawns, we need full attack information including king
bonus += evaluate_passed_pawns<WHITE>(pos, ei)
score += evaluate_passed_pawns<WHITE>(pos, ei)
- evaluate_passed_pawns<BLACK>(pos, ei);
// If one side has only a king, check whether exists any unstoppable passed pawn
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
bonus += evaluate_unstoppable_pawns<HasPopCnt>(pos, ei);
score += evaluate_unstoppable_pawns<HasPopCnt>(pos, ei);
// Evaluate space for both sides, only in middle-game.
if (mi->space_weight())
if (ei.mi->space_weight())
{
int s = evaluate_space<WHITE, HasPopCnt>(pos, ei) - evaluate_space<BLACK, HasPopCnt>(pos, ei);
bonus += apply_weight(make_score(s * mi->space_weight(), 0), Weights[Space]);
score += apply_weight(make_score(s * ei.mi->space_weight(), 0), Weights[Space]);
}
// Scale winning side if position is more drawish that what it appears
ScaleFactor sf = eg_value(bonus) > VALUE_DRAW ? mi->scale_factor(pos, WHITE)
: mi->scale_factor(pos, BLACK);
Phase phase = mi->game_phase();
ScaleFactor sf = eg_value(score) > VALUE_DRAW ? ei.mi->scale_factor(pos, WHITE)
: ei.mi->scale_factor(pos, BLACK);
// If we don't already have an unusual scale factor, check for opposite
// colored bishop endgames, and use a lower scale for those.
if ( phase < PHASE_MIDGAME
if ( ei.mi->game_phase() < PHASE_MIDGAME
&& pos.opposite_colored_bishops()
&& sf == SCALE_FACTOR_NORMAL)
{
@@ -372,46 +364,37 @@ Value do_evaluate(const Position& pos, Value& margin) {
// Interpolate between the middle game and the endgame score
margin = margins[pos.side_to_move()];
Value v = scale_by_game_phase(bonus, phase, sf);
Value v = scale_by_game_phase(score, ei.mi->game_phase(), sf);
// In case of tracing add all single evaluation contributions for both white and black
if (Trace)
{
trace_add(PST, pos.value());
trace_add(IMBALANCE, ei.mi->material_value());
trace_add(PAWN, ei.pi->pawns_value());
trace_add(MOBILITY, apply_weight(mobilityWhite, Weights[Mobility]), apply_weight(mobilityBlack, Weights[Mobility]));
trace_add(THREAT, evaluate_threats<WHITE>(pos, ei), evaluate_threats<BLACK>(pos, ei));
trace_add(PASSED, evaluate_passed_pawns<WHITE>(pos, ei), evaluate_passed_pawns<BLACK>(pos, ei));
trace_add(UNSTOPPABLE, evaluate_unstoppable_pawns<false>(pos, ei));
Score w = make_score(ei.mi->space_weight() * evaluate_space<WHITE, false>(pos, ei), 0);
Score b = make_score(ei.mi->space_weight() * evaluate_space<BLACK, false>(pos, ei), 0);
trace_add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
trace_add(TOTAL, score);
TraceStream << "\nUncertainty margin: White: " << to_cp(margins[WHITE])
<< ", Black: " << to_cp(margins[BLACK])
<< "\nScaling: " << std::noshowpos
<< std::setw(6) << 100.0 * ei.mi->game_phase() / 128.0 << "% MG, "
<< std::setw(6) << 100.0 * (1.0 - ei.mi->game_phase() / 128.0) << "% * "
<< std::setw(6) << (100.0 * sf) / SCALE_FACTOR_NORMAL << "% EG.\n"
<< "Total evaluation: " << to_cp(v);
}
return pos.side_to_move() == WHITE ? v : -v;
}
} // namespace
/// init_eval() initializes various tables used by the evaluation function
void init_eval(int threads) {
assert(threads <= MAX_THREADS);
for (int i = 0; i < MAX_THREADS; i++)
{
if (i >= threads)
{
delete PawnTable[i];
delete MaterialTable[i];
PawnTable[i] = NULL;
MaterialTable[i] = NULL;
continue;
}
if (!PawnTable[i])
PawnTable[i] = new PawnInfoTable();
if (!MaterialTable[i])
MaterialTable[i] = new MaterialInfoTable();
}
}
/// quit_eval() releases heap-allocated memory at program termination
void quit_eval() {
init_eval(0);
}
/// read_weights() reads evaluation weights from the corresponding UCI parameters
void read_evaluation_uci_options(Color us) {
@@ -422,7 +405,6 @@ void read_evaluation_uci_options(Color us) {
const int kingDangerThem = (us == WHITE ? KingDangerThem : KingDangerUs);
Weights[Mobility] = weight_option("Mobility (Middle Game)", "Mobility (Endgame)", WeightsInternal[Mobility]);
Weights[PawnStructure] = weight_option("Pawn Structure (Middle Game)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]);
Weights[PassedPawns] = weight_option("Passed Pawns (Middle Game)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]);
Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]);
Weights[kingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
@@ -492,14 +474,14 @@ namespace {
// evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color
template<PieceType Piece, Color Us, bool HasPopCnt>
template<PieceType Piece, Color Us, bool HasPopCnt, bool Trace>
Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) {
Bitboard b;
Square s, ksq;
int mob;
File f;
Score bonus = SCORE_ZERO;
Score score = SCORE_ZERO;
const BitCountType Full = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64 : CNT32;
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
@@ -542,18 +524,39 @@ namespace {
// Decrease score if we are attacked by an enemy pawn. Remaining part
// of threat evaluation must be done later when we have full attack info.
if (bit_is_set(ei.attackedBy[Them][PAWN], s))
bonus -= ThreatedByPawnPenalty[Piece];
score -= ThreatenedByPawnPenalty[Piece];
// Bishop and knight outposts squares
if ((Piece == BISHOP || Piece == KNIGHT) && pos.square_is_weak(s, Us))
bonus += evaluate_outposts<Piece, Us>(pos, ei, s);
score += evaluate_outposts<Piece, Us>(pos, ei, s);
// Queen or rook on 7th rank
if ( (Piece == ROOK || Piece == QUEEN)
&& relative_rank(Us, s) == RANK_7
&& relative_rank(Us, pos.king_square(Them)) == RANK_8)
{
bonus += (Piece == ROOK ? RookOn7thBonus : QueenOn7thBonus);
score += (Piece == ROOK ? RookOn7thBonus : QueenOn7thBonus);
}
// Special extra evaluation for bishops
if (Piece == BISHOP && pos.is_chess960())
{
// An important Chess960 pattern: A cornered bishop blocked by
// a friendly pawn diagonally in front of it is a very serious
// problem, especially when that pawn is also blocked.
if (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))
{
Square d = pawn_push(Us) + (square_file(s) == FILE_A ? DELTA_E : DELTA_W);
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
{
if (!pos.square_is_empty(s + d + pawn_push(Us)))
score -= 2*TrappedBishopA1H1Penalty;
else if (pos.piece_on(s + 2*d) == make_piece(Us, PAWN))
score -= TrappedBishopA1H1Penalty;
else
score -= TrappedBishopA1H1Penalty / 2;
}
}
}
// Special extra evaluation for rooks
@@ -564,9 +567,9 @@ namespace {
if (ei.pi->file_is_half_open(Us, f))
{
if (ei.pi->file_is_half_open(Them, f))
bonus += RookOpenFileBonus;
score += RookOpenFileBonus;
else
bonus += RookHalfOpenFileBonus;
score += RookHalfOpenFileBonus;
}
// Penalize rooks which are trapped inside a king. Penalize more if
@@ -582,7 +585,7 @@ namespace {
{
// Is there a half-open file between the king and the edge of the board?
if (!ei.pi->has_open_file_to_right(Us, square_file(ksq)))
bonus -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2
score -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2
: (TrappedRookPenalty - mob * 16), 0);
}
else if ( square_file(ksq) <= FILE_D
@@ -591,12 +594,16 @@ namespace {
{
// Is there a half-open file between the king and the edge of the board?
if (!ei.pi->has_open_file_to_left(Us, square_file(ksq)))
bonus -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2
score -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2
: (TrappedRookPenalty - mob * 16), 0);
}
}
}
return bonus;
if (Trace)
TracedScores[Us][Piece] = score;
return score;
}
@@ -609,7 +616,7 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b;
Score bonus = SCORE_ZERO;
Score score = SCORE_ZERO;
// Enemy pieces not defended by a pawn and under our attack
Bitboard weakEnemies = pos.pieces_of_color(Them)
@@ -627,41 +634,41 @@ namespace {
if (b)
for (PieceType pt2 = PAWN; pt2 < KING; pt2++)
if (b & pos.pieces(pt2))
bonus += ThreatBonus[pt1][pt2];
score += ThreatBonus[pt1][pt2];
}
return bonus;
return score;
}
// evaluate_pieces_of_color<>() assigns bonuses and penalties to all the
// pieces of a given color.
template<Color Us, bool HasPopCnt>
template<Color Us, bool HasPopCnt, bool Trace>
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Score bonus = mobility = SCORE_ZERO;
Score score = mobility = SCORE_ZERO;
// Do not include in mobility squares protected by enemy pawns or occupied by our pieces
const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces_of_color(Us));
bonus += evaluate_pieces<KNIGHT, Us, HasPopCnt>(pos, ei, mobility, mobilityArea);
bonus += evaluate_pieces<BISHOP, Us, HasPopCnt>(pos, ei, mobility, mobilityArea);
bonus += evaluate_pieces<ROOK, Us, HasPopCnt>(pos, ei, mobility, mobilityArea);
bonus += evaluate_pieces<QUEEN, Us, HasPopCnt>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<KNIGHT, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<BISHOP, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<ROOK, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<QUEEN, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
// Sum up all attacked squares
ei.attackedBy[Us][0] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
return bonus;
return score;
}
// evaluate_king<>() assigns bonuses and penalties to a king of a given color
template<Color Us, bool HasPopCnt>
template<Color Us, bool HasPopCnt, bool Trace>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
@@ -672,7 +679,7 @@ namespace {
const Square ksq = pos.king_square(Us);
// King shelter
Score bonus = ei.pi->king_shelter<Us>(pos, ksq);
Score score = ei.pi->king_shelter<Us>(pos, ksq);
// King safety. This is quite complicated, and is almost certainly far
// from optimally tuned.
@@ -762,10 +769,14 @@ namespace {
// value that will be used for pruning because this value can sometimes
// be very big, and so capturing a single attacking piece can therefore
// result in a score change far bigger than the value of the captured piece.
bonus -= KingDangerTable[Us][attackUnits];
score -= KingDangerTable[Us][attackUnits];
margins[Us] += mg_value(KingDangerTable[Us][attackUnits]);
}
return bonus;
if (Trace)
TracedScores[Us][KING] = score;
return score;
}
@@ -776,9 +787,10 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Score bonus = SCORE_ZERO;
Bitboard squaresToQueen, defendedSquares, unsafeSquares, supportingPawns;
Bitboard b = ei.pi->passed_pawns(Us);
Bitboard b, squaresToQueen, defendedSquares, unsafeSquares, supportingPawns;
Score score = SCORE_ZERO;
b = ei.pi->passed_pawns(Us);
if (!b)
return SCORE_ZERO;
@@ -800,9 +812,12 @@ namespace {
Square blockSq = s + pawn_push(Us);
// Adjust bonus based on kings proximity
ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 3 * rr);
ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr);
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 6 * rr);
ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 3 * rr);
// If blockSq is not the queening square then consider also a second push
if (square_rank(blockSq) != (Us == WHITE ? RANK_8 : RANK_1))
ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr);
// If the pawn is free to advance, increase bonus
if (pos.square_is_empty(blockSq))
@@ -813,8 +828,8 @@ namespace {
// If there is an enemy rook or queen attacking the pawn from behind,
// add all X-ray attacks by the rook or queen. Otherwise consider only
// the squares in the pawn's path attacked or occupied by the enemy.
if ( (squares_behind(Us, s) & pos.pieces(ROOK, QUEEN, Them))
&& (squares_behind(Us, s) & pos.pieces(ROOK, QUEEN, Them) & pos.attacks_from<ROOK>(s)))
if ( (squares_in_front_of(Them, s) & pos.pieces(ROOK, QUEEN, Them))
&& (squares_in_front_of(Them, s) & pos.pieces(ROOK, QUEEN, Them) & pos.attacks_from<ROOK>(s)))
unsafeSquares = squaresToQueen;
else
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][0] | pos.pieces_of_color(Them));
@@ -858,174 +873,176 @@ namespace {
else if (pos.pieces(ROOK, QUEEN, Them))
ebonus -= ebonus / 4;
}
bonus += make_score(mbonus, ebonus);
score += make_score(mbonus, ebonus);
} while (b);
// Add the scores to the middle game and endgame eval
return apply_weight(bonus, Weights[PassedPawns]);
return apply_weight(score, Weights[PassedPawns]);
}
// evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides
// evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite
// conservative and returns a winning score only when we are very sure that the pawn is winning.
template<bool HasPopCnt>
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
// Step 1. Hunt for unstoppable pawns. If we find at least one, record how many plies
// are required for promotion
int pliesToGo[2] = {256, 256};
Bitboard b, b2, blockers, supporters, queeningPath, candidates;
Square s, blockSq, queeningSquare;
Color c, winnerSide, loserSide;
bool pathDefended, opposed;
int pliesToGo, movesToGo, oppMovesToGo, sacptg, blockersCount, minKingDist, kingptg, d;
int pliesToQueen[] = { 256, 256 };
for (Color c = WHITE; c <= BLACK; c++)
// Step 1. Hunt for unstoppable passed pawns. If we find at least one,
// record how many plies are required for promotion.
for (c = WHITE; c <= BLACK; c++)
{
// Skip if other side has non-pawn pieces
if (pos.non_pawn_material(opposite_color(c)))
continue;
Bitboard b = ei.pi->passed_pawns(c);
b = ei.pi->passed_pawns(c);
while (b)
{
Square s = pop_1st_bit(&b);
Square queeningSquare = relative_square(c, make_square(square_file(s), RANK_8));
s = pop_1st_bit(&b);
queeningSquare = relative_square(c, make_square(square_file(s), RANK_8));
queeningPath = squares_in_front_of(c, s);
int mtg = RANK_8 - relative_rank(c, s) - int(relative_rank(c, s) == RANK_2);
int oppmtg = square_distance(pos.king_square(opposite_color(c)), queeningSquare) - int(c != pos.side_to_move());
bool pathDefended = ((ei.attackedBy[c][0] & squares_in_front_of(c, s)) == squares_in_front_of(c, s));
// Compute plies to queening and check direct advancement
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2);
oppMovesToGo = square_distance(pos.king_square(opposite_color(c)), queeningSquare) - int(c != pos.side_to_move());
pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath);
if (mtg >= oppmtg && !pathDefended)
if (movesToGo >= oppMovesToGo && !pathDefended)
continue;
int blockerCount = count_1s<Max15>(squares_in_front_of(c, s) & pos.occupied_squares());
mtg += blockerCount;
// Opponent king cannot block because path is defended and position
// is not in check. So only friendly pieces can be blockers.
assert(!pos.in_check());
assert((queeningPath & pos.occupied_squares()) == (queeningPath & pos.pieces_of_color(c)));
if (mtg >= oppmtg && !pathDefended)
// Add moves needed to free the path from friendly pieces and retest condition
movesToGo += count_1s<Max15>(queeningPath & pos.pieces_of_color(c));
if (movesToGo >= oppMovesToGo && !pathDefended)
continue;
int ptg = 2 * mtg - int(c == pos.side_to_move());
if (ptg < pliesToGo[c])
pliesToGo[c] = ptg;
pliesToGo = 2 * movesToGo - int(c == pos.side_to_move());
pliesToQueen[c] = Min(pliesToQueen[c], pliesToGo);
}
}
// Step 2. If either side cannot promote at least three plies before the other side then
// situation becomes too complex and we give up. Otherwise we determine the possibly "winning side"
if (abs(pliesToGo[WHITE] - pliesToGo[BLACK]) < 3)
return make_score(0, 0);
// Step 2. If either side cannot promote at least three plies before the other side then situation
// becomes too complex and we give up. Otherwise we determine the possibly "winning side"
if (abs(pliesToQueen[WHITE] - pliesToQueen[BLACK]) < 3)
return SCORE_ZERO;
Color winnerSide = (pliesToGo[WHITE] < pliesToGo[BLACK] ? WHITE : BLACK);
Color loserSide = opposite_color(winnerSide);
winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK);
loserSide = opposite_color(winnerSide);
// Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss?
// We collect the potential candidates in potentialBB.
Bitboard pawnBB = pos.pieces(PAWN, loserSide);
Bitboard potentialBB = pawnBB;
const Bitboard passedBB = ei.pi->passed_pawns(loserSide);
b = candidates = pos.pieces(PAWN, loserSide);
while(pawnBB)
while (b)
{
Square psq = pop_1st_bit(&pawnBB);
s = pop_1st_bit(&b);
// Check direct advancement
int mtg = RANK_8 - relative_rank(loserSide, psq) - int(relative_rank(loserSide, psq) == RANK_2);
int ptg = 2 * mtg - int(loserSide == pos.side_to_move());
// Compute plies from queening
queeningSquare = relative_square(loserSide, make_square(square_file(s), RANK_8));
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
// Check if (without even considering any obstacles) we're too far away
if (pliesToGo[winnerSide] + 3 <= ptg)
{
clear_bit(&potentialBB, psq);
continue;
}
// If this is passed pawn, then it _may_ promote in time. We give up.
if (bit_is_set(passedBB, psq))
return make_score(0, 0);
// Doubled pawn is worthless
if (squares_in_front_of(loserSide, psq) & (pos.pieces(PAWN, loserSide)))
{
clear_bit(&potentialBB, psq);
continue;
}
// Check if (without even considering any obstacles) we're too far away or doubled
if ( pliesToQueen[winnerSide] + 3 <= pliesToGo
|| (squares_in_front_of(loserSide, s) & pos.pieces(PAWN, loserSide)))
clear_bit(&candidates, s);
}
// Step 4. Check new passed pawn creation through king capturing and sacrifises
pawnBB = potentialBB;
// If any candidate is already a passed pawn it _may_ promote in time. We give up.
if (candidates & ei.pi->passed_pawns(loserSide))
return SCORE_ZERO;
while(pawnBB)
// Step 4. Check new passed pawn creation through king capturing and pawn sacrifices
b = candidates;
while (b)
{
Square psq = pop_1st_bit(&pawnBB);
s = pop_1st_bit(&b);
sacptg = blockersCount = 0;
minKingDist = kingptg = 256;
int mtg = RANK_8 - relative_rank(loserSide, psq) - int(relative_rank(loserSide, psq) == RANK_2);
int ptg = 2 * mtg - int(loserSide == pos.side_to_move());
// Compute plies from queening
queeningSquare = relative_square(loserSide, make_square(square_file(s), RANK_8));
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
// Generate list of obstacles
Bitboard obsBB = passed_pawn_mask(loserSide, psq) & pos.pieces(PAWN, winnerSide);
const bool pawnIsOpposed = squares_in_front_of(loserSide, psq) & obsBB;
assert(obsBB);
// Generate list of blocking pawns and supporters
supporters = neighboring_files_bb(s) & candidates;
opposed = squares_in_front_of(loserSide, s) & pos.pieces(PAWN, winnerSide);
blockers = passed_pawn_mask(loserSide, s) & pos.pieces(PAWN, winnerSide);
// How many plies does it take to remove all the obstacles?
int sacptg = 0;
int realObsCount = 0;
int minKingDist = 256;
assert(blockers);
while(obsBB)
// How many plies does it take to remove all the blocking pawns?
while (blockers)
{
Square obSq = pop_1st_bit(&obsBB);
int minMoves = 256;
blockSq = pop_1st_bit(&blockers);
movesToGo = 256;
// Check pawns that can give support to overcome obstacle (Eg. wp: a4,b4 bp: b2. b4 is giving support)
if (!pawnIsOpposed && square_file(psq) != square_file(obSq))
// Check pawns that can give support to overcome obstacle, for instance
// black pawns: a4, b4 white: b2 then pawn in b4 is giving support.
if (!opposed)
{
Bitboard supBB = in_front_bb(winnerSide, Square(obSq + (winnerSide == WHITE ? 8 : -8)))
& neighboring_files_bb(psq) & potentialBB;
b2 = supporters & in_front_bb(winnerSide, blockSq + pawn_push(winnerSide));
while(supBB) // This while-loop could be replaced with supSq = LSB/MSB(supBB) (depending on color)
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{
Square supSq = pop_1st_bit(&supBB);
int dist = square_distance(obSq, supSq);
minMoves = Min(minMoves, dist - 2);
d = square_distance(blockSq, pop_1st_bit(&b2)) - 2;
movesToGo = Min(movesToGo, d);
}
}
// Check pawns that can be sacrifised
Bitboard sacBB = passed_pawn_mask(winnerSide, obSq) & neighboring_files_bb(obSq) & potentialBB & ~(1ULL << psq);
// Check pawns that can be sacrificed against the blocking pawn
b2 = attack_span_mask(winnerSide, blockSq) & candidates & ~(1ULL << s);
while(sacBB) // This while-loop could be replaced with sacSq = LSB/MSB(sacBB) (depending on color)
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{
Square sacSq = pop_1st_bit(&sacBB);
int dist = square_distance(obSq, sacSq);
minMoves = Min(minMoves, dist - 2);
d = square_distance(blockSq, pop_1st_bit(&b2)) - 2;
movesToGo = Min(movesToGo, d);
}
// If obstacle can be destroyed with immediate pawn sacrifise, it's not real obstacle
if (minMoves <= 0)
// If obstacle can be destroyed with an immediate pawn exchange / sacrifice,
// it's not a real obstacle and we have nothing to add to pliesToGo.
if (movesToGo <= 0)
continue;
// Pawn sac calculations
sacptg += minMoves * 2;
// Plies needed to sacrifice against all the blocking pawns
sacptg += movesToGo * 2;
blockersCount++;
// King capture calc
realObsCount++;
int kingDist = square_distance(pos.king_square(loserSide), obSq);
minKingDist = Min(minKingDist, kingDist);
// Plies needed for the king to capture all the blocking pawns
d = square_distance(pos.king_square(loserSide), blockSq);
minKingDist = Min(minKingDist, d);
kingptg = (minKingDist + blockersCount) * 2;
}
// Check if pawn sac plan _may_ save the day
if (pliesToGo[winnerSide] + 3 > ptg + sacptg)
return make_score(0, 0);
// Check if pawn sacrifice plan _may_ save the day
if (pliesToQueen[winnerSide] + 3 > pliesToGo + sacptg)
return SCORE_ZERO;
// Check if king capture plan _may_ save the day (contains some false positives)
int kingptg = (minKingDist + realObsCount) * 2;
if (pliesToGo[winnerSide] + 3 > ptg + kingptg)
return make_score(0, 0);
if (pliesToQueen[winnerSide] + 3 > pliesToGo + kingptg)
return SCORE_ZERO;
}
// Step 5. Assign bonus
const int Sign[2] = {1, -1};
return Sign[winnerSide] * make_score(0, (Value) 0x500 - 0x20 * pliesToGo[winnerSide]);
// Winning pawn is unstoppable and will promote as first, return big score
Score score = make_score(0, (Value) 0x500 - 0x20 * pliesToQueen[winnerSide]);
return winnerSide == WHITE ? score : -score;
}
@@ -1061,8 +1078,8 @@ namespace {
// apply_weight() applies an evaluation weight to a value trying to prevent overflow
inline Score apply_weight(Score v, Score w) {
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
}
@@ -1075,11 +1092,9 @@ namespace {
assert(eg_value(v) > -VALUE_INFINITE && eg_value(v) < VALUE_INFINITE);
assert(ph >= PHASE_ENDGAME && ph <= PHASE_MIDGAME);
Value eg = eg_value(v);
Value ev = Value((eg * int(sf)) / SCALE_FACTOR_NORMAL);
int ev = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL;
int result = (mg_value(v) * int(ph) + ev * int(128 - ph)) / 128;
return Value(result & ~(GrainSize - 1));
return Value((result + GrainSize / 2) & ~(GrainSize - 1));
}
@@ -1121,4 +1136,86 @@ namespace {
for (int i = 0; i < 100; i++)
KingDangerTable[c][i] = apply_weight(make_score(t[i], 0), Weights[KingDangerUs + c]);
}
// A couple of little helpers used by tracing code, to_cp() converts a value to
// a double in centipawns scale, trace_add() stores white and black scores.
double to_cp(Value v) { return double(v) / double(PawnValueMidgame); }
void trace_add(int idx, Score wScore, Score bScore) {
TracedScores[WHITE][idx] = wScore;
TracedScores[BLACK][idx] = bScore;
}
// trace_row() is an helper function used by tracing code to register the
// values of a single evaluation term.
void trace_row(const char *name, int idx) {
Score wScore = TracedScores[WHITE][idx];
Score bScore = TracedScores[BLACK][idx];
switch (idx) {
case PST: case IMBALANCE: case PAWN: case UNSTOPPABLE: case TOTAL:
TraceStream << std::setw(20) << name << " | --- --- | --- --- | "
<< std::setw(6) << to_cp(mg_value(wScore)) << " "
<< std::setw(6) << to_cp(eg_value(wScore)) << " \n";
break;
default:
TraceStream << std::setw(20) << name << " | " << std::noshowpos
<< std::setw(5) << to_cp(mg_value(wScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore)) << " | "
<< std::setw(5) << to_cp(mg_value(bScore)) << " "
<< std::setw(5) << to_cp(eg_value(bScore)) << " | "
<< std::showpos
<< std::setw(6) << to_cp(mg_value(wScore - bScore)) << " "
<< std::setw(6) << to_cp(eg_value(wScore - bScore)) << " \n";
}
}
}
/// trace_evaluate() is like evaluate() but instead of a value returns a string
/// suitable to be print on stdout with the detailed descriptions and values of
/// each evaluation term. Used mainly for debugging.
std::string trace_evaluate(const Position& pos) {
Value margin;
std::string totals;
TraceStream.str("");
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
memset(TracedScores, 0, 2 * 16 * sizeof(Score));
do_evaluate<false, true>(pos, margin);
totals = TraceStream.str();
TraceStream.str("");
TraceStream << std::setw(21) << "Eval term " << "| White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n"
<< "---------------------+-------------+-------------+---------------\n";
trace_row("Material, PST, Tempo", PST);
trace_row("Material imbalance", IMBALANCE);
trace_row("Pawns", PAWN);
trace_row("Knights", KNIGHT);
trace_row("Bishops", BISHOP);
trace_row("Rooks", ROOK);
trace_row("Queens", QUEEN);
trace_row("Mobility", MOBILITY);
trace_row("King safety", KING);
trace_row("Threats", THREAT);
trace_row("Passed pawns", PASSED);
trace_row("Unstoppable pawns", UNSTOPPABLE);
trace_row("Space", SPACE);
TraceStream << "---------------------+-------------+-------------+---------------\n";
trace_row("Total", TOTAL);
TraceStream << totals;
return TraceStream.str();
}
+2 -5
View File
@@ -17,18 +17,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(EVALUATE_H_INCLUDED)
#define EVALUATE_H_INCLUDED
#include "color.h"
#include "value.h"
#include "types.h"
class Position;
extern Value evaluate(const Position& pos, Value& margin);
extern void init_eval(int threads);
extern void quit_eval();
extern std::string trace_evaluate(const Position& pos);
extern void read_evaluation_uci_options(Color sideToMove);
#endif // !defined(EVALUATE_H_INCLUDED)
-98
View File
@@ -1,98 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include <cstring>
#include "history.h"
////
//// Functions
////
/// Constructor
History::History() { clear(); }
/// History::clear() clears the history tables
void History::clear() {
memset(history, 0, 16 * 64 * sizeof(int));
memset(maxStaticValueDelta, 0, 16 * 64 * sizeof(int));
}
/// History::success() registers a move as being successful. This is done
/// whenever a non-capturing move causes a beta cutoff in the main search.
/// The three parameters are the moving piece, the destination square, and
/// the search depth.
void History::success(Piece p, Square to, Depth d) {
assert(piece_is_ok(p));
assert(square_is_ok(to));
history[p][to] += int(d) * int(d);
// Prevent history overflow
if (history[p][to] >= HistoryMax)
for (int i = 0; i < 16; i++)
for (int j = 0; j < 64; j++)
history[i][j] /= 2;
}
/// History::failure() registers a move as being unsuccessful. The function is
/// called for each non-capturing move which failed to produce a beta cutoff
/// at a node where a beta cutoff was finally found.
void History::failure(Piece p, Square to, Depth d) {
assert(piece_is_ok(p));
assert(square_is_ok(to));
history[p][to] -= int(d) * int(d);
// Prevent history underflow
if (history[p][to] <= -HistoryMax)
for (int i = 0; i < 16; i++)
for (int j = 0; j < 64; j++)
history[i][j] /= 2;
}
/// History::set_gain() and History::gain() store and retrieve the
/// gain of a move given the delta of the static position evaluations
/// before and after the move.
void History::set_gain(Piece p, Square to, Value delta) {
if (delta >= maxStaticValueDelta[p][to])
maxStaticValueDelta[p][to] = delta;
else
maxStaticValueDelta[p][to]--;
}
+23 -42
View File
@@ -17,23 +17,11 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(HISTORY_H_INCLUDED)
#define HISTORY_H_INCLUDED
////
//// Includes
////
#include "depth.h"
#include "move.h"
#include "piece.h"
#include "value.h"
////
//// Types
////
#include <cstring>
#include "types.h"
/// The History class stores statistics about how often different moves
/// have been successful or unsuccessful during the current search. These
@@ -45,45 +33,38 @@
class History {
public:
History();
void clear();
void success(Piece p, Square to, Depth d);
void failure(Piece p, Square to, Depth d);
int value(Piece p, Square to) const;
void set_gain(Piece p, Square to, Value delta);
Value value(Piece p, Square to) const;
void update(Piece p, Square to, Value bonus);
Value gain(Piece p, Square to) const;
void update_gain(Piece p, Square to, Value g);
static const Value MaxValue = Value(2000);
private:
int history[16][64]; // [piece][square]
int maxStaticValueDelta[16][64]; // [piece][from_square][to_square]
Value history[16][64]; // [piece][to_square]
Value maxGains[16][64]; // [piece][to_square]
};
inline void History::clear() {
memset(history, 0, 16 * 64 * sizeof(Value));
memset(maxGains, 0, 16 * 64 * sizeof(Value));
}
////
//// Constants and variables
////
/// HistoryMax controls how often the history counters will be scaled down:
/// When the history score for a move gets bigger than HistoryMax, all
/// entries in the table are divided by 2. It is difficult to guess what
/// the ideal value of this constant is. Scaling down the scores often has
/// the effect that parts of the search tree which have been searched
/// recently have a bigger importance for move ordering than the moves which
/// have been searched a long time ago.
const int HistoryMax = 50000 * ONE_PLY;
////
//// Inline functions
////
inline int History::value(Piece p, Square to) const {
inline Value History::value(Piece p, Square to) const {
return history[p][to];
}
inline void History::update(Piece p, Square to, Value bonus) {
if (abs(history[p][to] + bonus) < MaxValue) history[p][to] += bonus;
}
inline Value History::gain(Piece p, Square to) const {
return Value(maxStaticValueDelta[p][to]);
return maxGains[p][to];
}
inline void History::update_gain(Piece p, Square to, Value g) {
maxGains[p][to] = Max(g, maxGains[p][to] - 1);
}
#endif // !defined(HISTORY_H_INCLUDED)
+21 -2
View File
@@ -17,11 +17,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(LOCK_H_INCLUDED)
#define LOCK_H_INCLUDED
#if !defined(_MSC_VER)
# include <pthread.h>
@@ -44,6 +42,25 @@ typedef pthread_cond_t WaitCondition;
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
// Default fast and race free locks and condition variables
#if !defined(OLD_LOCKS)
typedef SRWLOCK Lock;
typedef CONDITION_VARIABLE WaitCondition;
# define lock_init(x) InitializeSRWLock(x)
# define lock_grab(x) AcquireSRWLockExclusive(x)
# define lock_release(x) ReleaseSRWLockExclusive(x)
# define lock_destroy(x) (x)
# define cond_destroy(x) (x)
# define cond_init(x) InitializeConditionVariable(x)
# define cond_signal(x) WakeConditionVariable(x)
# define cond_wait(x,y) SleepConditionVariableSRW(x, y, INFINITE,0)
// Fallback solution to build for Windows XP and older versions, note that
// cond_wait() is racy between lock_release() and WaitForSingleObject().
#else
typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition;
@@ -57,4 +74,6 @@ typedef HANDLE WaitCondition;
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(*x, INFINITE); lock_grab(y); }
#endif
#endif
#endif // !defined(LOCK_H_INCLUDED)
+20 -43
View File
@@ -20,23 +20,15 @@
// To profile with callgrind uncomment following line
//#define USE_CALLGRIND
////
//// Includes
////
#include <cstdio>
#include <iostream>
#include <string>
#include "bitboard.h"
#include "bitcount.h"
#include "endgame.h"
#include "evaluate.h"
#include "material.h"
#include "misc.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "search.h"
#include "ucioption.h"
#ifdef USE_CALLGRIND
@@ -47,64 +39,49 @@ using namespace std;
extern bool execute_uci_command(const string& cmd);
extern void benchmark(int argc, char* argv[]);
////
//// Functions
////
extern void init_kpk_bitbase();
int main(int argc, char* argv[]) {
// Disable IO buffering
// Disable IO buffering for C and C++ standard libraries
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
cout.rdbuf()->pubsetbuf(NULL, 0);
cin.rdbuf()->pubsetbuf(NULL, 0);
// Startup initializations
init_bitboards();
init_uci_options();
Position::init_zobrist();
Position::init_piece_square_tables();
init_eval(1);
init_bitbases();
init_kpk_bitbase();
init_search();
init_threads();
Threads.init();
#ifdef USE_CALLGRIND
CALLGRIND_START_INSTRUMENTATION;
#endif
if (argc <= 1)
if (argc < 2)
{
// Print copyright notice
cout << engine_name()
<< " by Tord Romstad, Marco Costalba, Joona Kiiski" << endl;
cout << engine_name() << " by " << engine_authors() << endl;
if (CpuHasPOPCNT)
cout << "Good! CPU has hardware POPCNT." << endl;
// Wait for a command from the user, and passes this command to
// execute_uci_command() and also intercepts EOF from stdin, by
// translating EOF to the "quit" command. This ensures that we
// exit gracefully if the GUI dies unexpectedly.
// execute_uci_command() and also intercepts EOF from stdin to
// ensure that we exit gracefully if the GUI dies unexpectedly.
string cmd;
do {
// Wait for a command from stdin
if (!getline(cin, cmd))
cmd = "quit";
} while (execute_uci_command(cmd));
}
else // Process command line arguments
{
if (string(argv[1]) != "bench" || argc > 7)
cout << "Usage: stockfish bench [hash size = 128] [threads = 1] "
<< "[limit = 12] [fen positions file = default] "
<< "[depth, time, perft or node limited = depth]" << endl;
else
benchmark(argc, argv);
while (getline(cin, cmd) && execute_uci_command(cmd)) {}
}
else if (string(argv[1]) == "bench" && argc < 8)
benchmark(argc, argv);
else
cout << "Usage: stockfish bench [hash size = 128] [threads = 1] "
<< "[limit = 12] [fen positions file = default] "
<< "[limited by depth, time, nodes or perft = depth]" << endl;
exit_threads();
quit_eval();
Threads.exit();
return 0;
}
+101 -251
View File
@@ -17,30 +17,22 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include <cstring>
#include <map>
#include "material.h"
using namespace std;
////
//// Local definitions
////
namespace {
// Values modified by Joona Kiiski
const Value MidgameLimit = Value(15581);
const Value EndgameLimit = Value(3998);
// Scale factors used when one side has no more pawns
const int NoPawnsSF[4] = { 6, 12, 32 };
// Polynomial material balance parameters
const Value RedundantQueenPenalty = Value(320);
const Value RedundantRookPenalty = Value(554);
@@ -55,19 +47,15 @@ namespace {
{ 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 },
{ 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } };
typedef EndgameEvaluationFunctionBase EF;
typedef EndgameScalingFunctionBase SF;
typedef map<Key, EF*> EFMap;
typedef map<Key, SF*> SFMap;
// Endgame evaluation and scaling functions accessed direcly and not through
// the function maps because correspond to more then one material hash key.
EvaluationFunction<KmmKm> EvaluateKmmKm[] = { EvaluationFunction<KmmKm>(WHITE), EvaluationFunction<KmmKm>(BLACK) };
EvaluationFunction<KXK> EvaluateKXK[] = { EvaluationFunction<KXK>(WHITE), EvaluationFunction<KXK>(BLACK) };
ScalingFunction<KBPsK> ScaleKBPsK[] = { ScalingFunction<KBPsK>(WHITE), ScalingFunction<KBPsK>(BLACK) };
ScalingFunction<KQKRPs> ScaleKQKRPs[] = { ScalingFunction<KQKRPs>(WHITE), ScalingFunction<KQKRPs>(BLACK) };
ScalingFunction<KPsK> ScaleKPsK[] = { ScalingFunction<KPsK>(WHITE), ScalingFunction<KPsK>(BLACK) };
ScalingFunction<KPKP> ScaleKPKP[] = { ScalingFunction<KPKP>(WHITE), ScalingFunction<KPKP>(BLACK) };
Endgame<Value, KmmKm> EvaluateKmmKm[] = { Endgame<Value, KmmKm>(WHITE), Endgame<Value, KmmKm>(BLACK) };
Endgame<Value, KXK> EvaluateKXK[] = { Endgame<Value, KXK>(WHITE), Endgame<Value, KXK>(BLACK) };
Endgame<ScaleFactor, KBPsK> ScaleKBPsK[] = { Endgame<ScaleFactor, KBPsK>(WHITE), Endgame<ScaleFactor, KBPsK>(BLACK) };
Endgame<ScaleFactor, KQKRPs> ScaleKQKRPs[] = { Endgame<ScaleFactor, KQKRPs>(WHITE), Endgame<ScaleFactor, KQKRPs>(BLACK) };
Endgame<ScaleFactor, KPsK> ScaleKPsK[] = { Endgame<ScaleFactor, KPsK>(WHITE), Endgame<ScaleFactor, KPsK>(BLACK) };
Endgame<ScaleFactor, KPKP> ScaleKPKP[] = { Endgame<ScaleFactor, KPKP>(WHITE), Endgame<ScaleFactor, KPKP>(BLACK) };
// Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) {
@@ -77,7 +65,7 @@ namespace {
&& pos.non_pawn_material(Us) >= RookValueMidgame;
}
template<Color Us> bool is_KBPsK(const Position& pos) {
template<Color Us> bool is_KBPsKs(const Position& pos) {
return pos.non_pawn_material(Us) == BishopValueMidgame
&& pos.piece_count(Us, BISHOP) == 1
&& pos.piece_count(Us, PAWN) >= 1;
@@ -91,86 +79,15 @@ namespace {
&& pos.piece_count(Them, ROOK) == 1
&& pos.piece_count(Them, PAWN) >= 1;
}
}
} // namespace
////
//// Classes
////
/// MaterialInfoTable c'tor and d'tor allocate and free the space for Endgames
/// EndgameFunctions class stores endgame evaluation and scaling functions
/// in two std::map. Because STL library is not guaranteed to be thread
/// safe even for read access, the maps, although with identical content,
/// are replicated for each thread. This is faster then using locks.
void MaterialInfoTable::init() { Base::init(); if (!funcs) funcs = new Endgames(); }
MaterialInfoTable::~MaterialInfoTable() { delete funcs; }
class EndgameFunctions {
public:
EndgameFunctions();
~EndgameFunctions();
template<class T> T* get(Key key) const;
private:
template<class T> void add(const string& keyCode);
static Key buildKey(const string& keyCode);
static const string swapColors(const string& keyCode);
// Here we store two maps, for evaluate and scaling functions...
pair<EFMap, SFMap> maps;
// ...and here is the accessing template function
template<typename T> const map<Key, T*>& get() const;
};
// Explicit specializations of a member function shall be declared in
// the namespace of which the class template is a member.
template<> const EFMap& EndgameFunctions::get<EF>() const { return maps.first; }
template<> const SFMap& EndgameFunctions::get<SF>() const { return maps.second; }
////
//// Functions
////
/// MaterialInfoTable c'tor and d'tor, called once by each thread
MaterialInfoTable::MaterialInfoTable() {
entries = new MaterialInfo[MaterialTableSize];
funcs = new EndgameFunctions();
if (!entries || !funcs)
{
cerr << "Failed to allocate " << MaterialTableSize * sizeof(MaterialInfo)
<< " bytes for material hash table." << endl;
exit(EXIT_FAILURE);
}
memset(entries, 0, MaterialTableSize * sizeof(MaterialInfo));
}
MaterialInfoTable::~MaterialInfoTable() {
delete funcs;
delete [] entries;
}
/// MaterialInfoTable::game_phase() calculates the phase given the current
/// position. Because the phase is strictly a function of the material, it
/// is stored in MaterialInfo.
Phase MaterialInfoTable::game_phase(const Position& pos) {
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
if (npm >= MidgameLimit)
return PHASE_MIDGAME;
if (npm <= EndgameLimit)
return PHASE_ENDGAME;
return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
}
/// MaterialInfoTable::get_material_info() takes a position object as input,
/// computes or looks up a MaterialInfo object, and returns a pointer to it.
@@ -178,11 +95,10 @@ Phase MaterialInfoTable::game_phase(const Position& pos) {
/// is stored there, so we don't have to recompute everything when the
/// same material configuration occurs again.
MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
Key key = pos.get_material_key();
unsigned index = unsigned(key & (MaterialTableSize - 1));
MaterialInfo* mi = entries + index;
MaterialInfo* mi = probe(key);
// If mi->key matches the position's material hash key, it means that we
// have analysed this material configuration before, and we can simply
@@ -190,10 +106,10 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
if (mi->key == key)
return mi;
// Clear the MaterialInfo object, and set its key
// Initialize MaterialInfo entry
memset(mi, 0, sizeof(MaterialInfo));
mi->factor[WHITE] = mi->factor[BLACK] = uint8_t(SCALE_FACTOR_NORMAL);
mi->key = key;
mi->factor[WHITE] = mi->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
// Store game phase
mi->gamePhase = MaterialInfoTable::game_phase(pos);
@@ -201,18 +117,22 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
// Let's look if we have a specialized evaluation function for this
// particular material configuration. First we look for a fixed
// configuration one, then a generic one if previous search failed.
if ((mi->evaluationFunction = funcs->get<EF>(key)) != NULL)
if ((mi->evaluationFunction = funcs->get<EndgameBase<Value> >(key)) != NULL)
return mi;
if (is_KXK<WHITE>(pos) || is_KXK<BLACK>(pos))
if (is_KXK<WHITE>(pos))
{
mi->evaluationFunction = is_KXK<WHITE>(pos) ? &EvaluateKXK[WHITE] : &EvaluateKXK[BLACK];
mi->evaluationFunction = &EvaluateKXK[WHITE];
return mi;
}
if ( pos.pieces(PAWN) == EmptyBoardBB
&& pos.pieces(ROOK) == EmptyBoardBB
&& pos.pieces(QUEEN) == EmptyBoardBB)
if (is_KXK<BLACK>(pos))
{
mi->evaluationFunction = &EvaluateKXK[BLACK];
return mi;
}
if (!pos.pieces(PAWN) && !pos.pieces(ROOK) && !pos.pieces(QUEEN))
{
// Minor piece endgame with at least one minor piece per side and
// no pawns. Note that the case KmmK is already handled by KXK.
@@ -232,9 +152,9 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
//
// We face problems when there are several conflicting applicable
// scaling functions and we need to decide which one to use.
SF* sf;
EndgameBase<ScaleFactor>* sf;
if ((sf = funcs->get<SF>(key)) != NULL)
if ((sf = funcs->get<EndgameBase<ScaleFactor> >(key)) != NULL)
{
mi->scalingFunction[sf->color()] = sf;
return mi;
@@ -243,10 +163,10 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
// Generic scaling functions that refer to more then one material
// distribution. Should be probed after the specialized ones.
// Note that these ones don't return after setting the function.
if (is_KBPsK<WHITE>(pos))
if (is_KBPsKs<WHITE>(pos))
mi->scalingFunction[WHITE] = &ScaleKBPsK[WHITE];
if (is_KBPsK<BLACK>(pos))
if (is_KBPsKs<BLACK>(pos))
mi->scalingFunction[BLACK] = &ScaleKBPsK[BLACK];
if (is_KQKRPs<WHITE>(pos))
@@ -255,7 +175,10 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
else if (is_KQKRPs<BLACK>(pos))
mi->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK];
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == VALUE_ZERO)
Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK);
if (npm_w + npm_b == VALUE_ZERO)
{
if (pos.piece_count(BLACK, PAWN) == 0)
{
@@ -276,160 +199,87 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
}
}
// Compute the space weight
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >=
2*QueenValueMidgame + 4*RookValueMidgame + 2*KnightValueMidgame)
// No pawns makes it difficult to win, even with a material advantage
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame)
{
int minorPieceCount = pos.piece_count(WHITE, KNIGHT)
+ pos.piece_count(BLACK, KNIGHT)
+ pos.piece_count(WHITE, BISHOP)
+ pos.piece_count(BLACK, BISHOP);
mi->factor[WHITE] = uint8_t
(npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[Min(pos.piece_count(WHITE, BISHOP), 2)]);
}
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame)
{
mi->factor[BLACK] = uint8_t
(npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[Min(pos.piece_count(BLACK, BISHOP), 2)]);
}
// Compute the space weight
if (npm_w + npm_b >= 2 * QueenValueMidgame + 4 * RookValueMidgame + 2 * KnightValueMidgame)
{
int minorPieceCount = pos.piece_count(WHITE, KNIGHT) + pos.piece_count(WHITE, BISHOP)
+ pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, BISHOP);
mi->spaceWeight = minorPieceCount * minorPieceCount;
}
// Evaluate the material balance
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
// for the bishop pair "extended piece", this allow us to be more flexible
// in defining bishop pair bonuses.
const int pieceCount[2][8] = {
{ pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
pos.piece_count(WHITE, BISHOP), pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
pos.piece_count(WHITE, BISHOP) , pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
pos.piece_count(BLACK, BISHOP), pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
pos.piece_count(BLACK, BISHOP) , pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
Color c, them;
int sign, pt1, pt2, pc;
int v, vv, matValue = 0;
for (c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign)
{
// No pawns makes it difficult to win, even with a material advantage
if ( pos.piece_count(c, PAWN) == 0
&& pos.non_pawn_material(c) - pos.non_pawn_material(opposite_color(c)) <= BishopValueMidgame)
{
if ( pos.non_pawn_material(c) == pos.non_pawn_material(opposite_color(c))
|| pos.non_pawn_material(c) < RookValueMidgame)
mi->factor[c] = 0;
else
{
switch (pos.piece_count(c, BISHOP)) {
case 2:
mi->factor[c] = 32;
break;
case 1:
mi->factor[c] = 12;
break;
case 0:
mi->factor[c] = 6;
break;
}
}
}
// Redundancy of major pieces, formula based on Kaufman's paper
// "The Evaluation of Material Imbalances in Chess"
// http://mywebpages.comcast.net/danheisman/Articles/evaluation_of_material_imbalance.htm
if (pieceCount[c][ROOK] >= 1)
matValue -= sign * ((pieceCount[c][ROOK] - 1) * RedundantRookPenalty + pieceCount[c][QUEEN] * RedundantQueenPenalty);
them = opposite_color(c);
v = 0;
// Second-degree polynomial material imbalance by Tord Romstad
//
// We use PIECE_TYPE_NONE as a place holder for the bishop pair "extended piece",
// this allow us to be more flexible in defining bishop pair bonuses.
for (pt1 = PIECE_TYPE_NONE; pt1 <= QUEEN; pt1++)
{
pc = pieceCount[c][pt1];
if (!pc)
continue;
vv = LinearCoefficients[pt1];
for (pt2 = PIECE_TYPE_NONE; pt2 <= pt1; pt2++)
vv += pieceCount[c][pt2] * QuadraticCoefficientsSameColor[pt1][pt2]
+ pieceCount[them][pt2] * QuadraticCoefficientsOppositeColor[pt1][pt2];
v += pc * vv;
}
matValue += sign * v;
}
mi->value = int16_t(matValue / 16);
mi->value = int16_t((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
return mi;
}
/// EndgameFunctions member definitions
/// MaterialInfoTable::imbalance() calculates imbalance comparing piece count of each
/// piece type for both colors.
EndgameFunctions::EndgameFunctions() {
template<Color Us>
int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
add<EvaluationFunction<KNNK> >("KNNK");
add<EvaluationFunction<KPK> >("KPK");
add<EvaluationFunction<KBNK> >("KBNK");
add<EvaluationFunction<KRKP> >("KRKP");
add<EvaluationFunction<KRKB> >("KRKB");
add<EvaluationFunction<KRKN> >("KRKN");
add<EvaluationFunction<KQKR> >("KQKR");
add<EvaluationFunction<KBBKN> >("KBBKN");
const Color Them = (Us == WHITE ? BLACK : WHITE);
add<ScalingFunction<KNPK> >("KNPK");
add<ScalingFunction<KRPKR> >("KRPKR");
add<ScalingFunction<KBPKB> >("KBPKB");
add<ScalingFunction<KBPPKB> >("KBPPKB");
add<ScalingFunction<KBPKN> >("KBPKN");
add<ScalingFunction<KRPPKRP> >("KRPPKRP");
int pt1, pt2, pc, v;
int value = 0;
// Redundancy of major pieces, formula based on Kaufman's paper
// "The Evaluation of Material Imbalances in Chess"
if (pieceCount[Us][ROOK] > 0)
value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1)
+ RedundantQueenPenalty * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = PIECE_TYPE_NONE; pt1 <= QUEEN; pt1++)
{
pc = pieceCount[Us][pt1];
if (!pc)
continue;
v = LinearCoefficients[pt1];
for (pt2 = PIECE_TYPE_NONE; pt2 <= pt1; pt2++)
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
value += pc * v;
}
return value;
}
EndgameFunctions::~EndgameFunctions() {
for (EFMap::const_iterator it = maps.first.begin(); it != maps.first.end(); ++it)
delete it->second;
/// MaterialInfoTable::game_phase() calculates the phase given the current
/// position. Because the phase is strictly a function of the material, it
/// is stored in MaterialInfo.
for (SFMap::const_iterator it = maps.second.begin(); it != maps.second.end(); ++it)
delete it->second;
}
Key EndgameFunctions::buildKey(const string& keyCode) {
assert(keyCode.length() > 0 && keyCode.length() < 8);
assert(keyCode[0] == 'K');
string fen;
bool upcase = false;
// Build up a fen string with the given pieces, note that
// the fen string could be of an illegal position.
for (size_t i = 0; i < keyCode.length(); i++)
{
if (keyCode[i] == 'K')
upcase = !upcase;
fen += char(upcase ? toupper(keyCode[i]) : tolower(keyCode[i]));
}
fen += char(8 - keyCode.length() + '0');
fen += "/8/8/8/8/8/8/8 w - -";
return Position(fen, 0).get_material_key();
}
const string EndgameFunctions::swapColors(const string& keyCode) {
// Build corresponding key for the opposite color: "KBPKN" -> "KNKBP"
size_t idx = keyCode.find('K', 1);
return keyCode.substr(idx) + keyCode.substr(0, idx);
}
template<class T>
void EndgameFunctions::add(const string& keyCode) {
typedef typename T::Base F;
typedef map<Key, F*> M;
const_cast<M&>(get<F>()).insert(pair<Key, F*>(buildKey(keyCode), new T(WHITE)));
const_cast<M&>(get<F>()).insert(pair<Key, F*>(buildKey(swapColors(keyCode)), new T(BLACK)));
}
template<class T>
T* EndgameFunctions::get(Key key) const {
typename map<Key, T*>::const_iterator it = get<T>().find(key);
return it != get<T>().end() ? it->second : NULL;
Phase MaterialInfoTable::game_phase(const Position& pos) {
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
return npm >= MidgameLimit ? PHASE_MIDGAME
: npm <= EndgameLimit ? PHASE_ENDGAME
: Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
}
+35 -80
View File
@@ -17,23 +17,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MATERIAL_H_INCLUDED)
#define MATERIAL_H_INCLUDED
////
//// Includes
////
#include "endgame.h"
#include "position.h"
#include "tt.h"
#include "types.h"
////
//// Types
////
const int MaterialTableSize = 1024;
const int MaterialTableSize = 8192;
/// MaterialInfo is a class which contains various information about a
/// material configuration. It contains a material balance evaluation,
@@ -61,49 +53,31 @@ private:
Key key;
int16_t value;
uint8_t factor[2];
EndgameEvaluationFunctionBase* evaluationFunction;
EndgameScalingFunctionBase* scalingFunction[2];
EndgameBase<Value>* evaluationFunction;
EndgameBase<ScaleFactor>* scalingFunction[2];
int spaceWeight;
Phase gamePhase;
};
/// The MaterialInfoTable class represents a pawn hash table. It is basically
/// just an array of MaterialInfo objects and a few methods for accessing these
/// objects. The most important method is get_material_info, which looks up a
/// position in the table and returns a pointer to a MaterialInfo object.
class EndgameFunctions;
class MaterialInfoTable {
MaterialInfoTable(const MaterialInfoTable&);
MaterialInfoTable& operator=(const MaterialInfoTable&);
/// The MaterialInfoTable class represents a pawn hash table. The most important
/// method is get_material_info, which returns a pointer to a MaterialInfo object.
class MaterialInfoTable : public SimpleHash<MaterialInfo, MaterialTableSize> {
public:
MaterialInfoTable();
~MaterialInfoTable();
MaterialInfo* get_material_info(const Position& pos);
void init();
MaterialInfo* get_material_info(const Position& pos) const;
static Phase game_phase(const Position& pos);
private:
MaterialInfo* entries;
EndgameFunctions* funcs;
template<Color Us>
static int imbalance(const int pieceCount[][8]);
Endgames* funcs;
};
////
//// Inline functions
////
/// MaterialInfo::material_value simply returns the material balance
/// evaluation that is independent from game phase.
inline Score MaterialInfo::material_value() const {
return make_score(value, value);
}
/// MaterialInfo::scale_factor takes a position and a color as input, and
/// returns a scale factor for the given color. We have to provide the
/// position in addition to the color, because the scale factor need not
@@ -113,50 +87,31 @@ inline Score MaterialInfo::material_value() const {
inline ScaleFactor MaterialInfo::scale_factor(const Position& pos, Color c) const {
if (scalingFunction[c] != NULL)
{
ScaleFactor sf = scalingFunction[c]->apply(pos);
if (sf != SCALE_FACTOR_NONE)
return sf;
}
return ScaleFactor(factor[c]);
if (!scalingFunction[c])
return ScaleFactor(factor[c]);
ScaleFactor sf = scalingFunction[c]->apply(pos);
return sf == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : sf;
}
/// MaterialInfo::space_weight() simply returns the weight for the space
/// evaluation for this material configuration.
inline int MaterialInfo::space_weight() const {
return spaceWeight;
}
/// MaterialInfo::game_phase() returns the game phase according
/// to this material configuration.
inline Phase MaterialInfo::game_phase() const {
return gamePhase;
}
/// MaterialInfo::specialized_eval_exists decides whether there is a
/// specialized evaluation function for the current material configuration,
/// or if the normal evaluation function should be used.
inline bool MaterialInfo::specialized_eval_exists() const {
return evaluationFunction != NULL;
}
/// MaterialInfo::evaluate applies a specialized evaluation function
/// to a given position object. It should only be called when
/// specialized_eval_exists() returns 'true'.
inline Value MaterialInfo::evaluate(const Position& pos) const {
return evaluationFunction->apply(pos);
}
inline Score MaterialInfo::material_value() const {
return make_score(value, value);
}
inline int MaterialInfo::space_weight() const {
return spaceWeight;
}
inline Phase MaterialInfo::game_phase() const {
return gamePhase;
}
inline bool MaterialInfo::specialized_eval_exists() const {
return evaluationFunction != NULL;
}
#endif // !defined(MATERIAL_H_INCLUDED)
+122 -142
View File
@@ -17,11 +17,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#if !defined(_MSC_VER)
# include <sys/time.h>
@@ -55,83 +50,18 @@
using namespace std;
/// Version number. If this is left empty, the current date (in the format
/// YYMMDD) is used as a version number.
/// Version number. If EngineVersion is left empty, then AppTag plus
/// current date (in the format YYMMDD) is used as a version number.
static const string EngineVersion = "2.0";
static const string AppName = "Stockfish";
static const string EngineVersion = "2.1.1";
static const string AppTag = "";
////
//// Variables
////
static uint64_t dbg_cnt0 = 0;
static uint64_t dbg_cnt1 = 0;
bool dbg_show_mean = false;
bool dbg_show_hit_rate = false;
////
//// Functions
////
void dbg_hit_on(bool b) {
assert(!dbg_show_mean);
dbg_show_hit_rate = true;
dbg_cnt0++;
if (b)
dbg_cnt1++;
}
void dbg_hit_on_c(bool c, bool b) {
if (c)
dbg_hit_on(b);
}
void dbg_before() {
assert(!dbg_show_mean);
dbg_show_hit_rate = true;
dbg_cnt0++;
}
void dbg_after() {
assert(!dbg_show_mean);
dbg_show_hit_rate = true;
dbg_cnt1++;
}
void dbg_mean_of(int v) {
assert(!dbg_show_hit_rate);
dbg_show_mean = true;
dbg_cnt0++;
dbg_cnt1 += v;
}
void dbg_print_hit_rate() {
cout << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
<< " hit rate (%) " << (dbg_cnt1*100)/(dbg_cnt0 ? dbg_cnt0 : 1) << endl;
}
void dbg_print_mean() {
cout << "Total " << dbg_cnt0 << " Mean "
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl;
}
/// engine_name() returns the full name of the current Stockfish version.
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when
/// the program was compiled) or "Stockfish <version number>", depending
/// on whether the constant EngineVersion (defined in misc.h) is empty.
/// on whether the constant EngineVersion is empty.
const string engine_name() {
@@ -155,62 +85,100 @@ const string engine_name() {
}
/// get_system_time() returns the current system time, measured in
/// milliseconds.
/// Our brave developers! Required by UCI
const string engine_authors() {
return "Tord Romstad, Marco Costalba and Joona Kiiski";
}
/// Debug stuff. Helper functions used mainly for debugging purposes
static uint64_t dbg_hit_cnt0;
static uint64_t dbg_hit_cnt1;
static uint64_t dbg_mean_cnt0;
static uint64_t dbg_mean_cnt1;
void dbg_print_hit_rate() {
if (dbg_hit_cnt0)
cout << "Total " << dbg_hit_cnt0 << " Hit " << dbg_hit_cnt1
<< " hit rate (%) " << 100 * dbg_hit_cnt1 / dbg_hit_cnt0 << endl;
}
void dbg_print_mean() {
if (dbg_mean_cnt0)
cout << "Total " << dbg_mean_cnt0 << " Mean "
<< (float)dbg_mean_cnt1 / dbg_mean_cnt0 << endl;
}
void dbg_mean_of(int v) {
dbg_mean_cnt0++;
dbg_mean_cnt1 += v;
}
void dbg_hit_on(bool b) {
dbg_hit_cnt0++;
if (b)
dbg_hit_cnt1++;
}
void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
void dbg_before() { dbg_hit_on(false); }
void dbg_after() { dbg_hit_on(true); dbg_hit_cnt0--; }
/// get_system_time() returns the current system time, measured in milliseconds
int get_system_time() {
#if defined(_MSC_VER)
struct _timeb t;
_ftime(&t);
return int(t.time*1000 + t.millitm);
struct _timeb t;
_ftime(&t);
return int(t.time * 1000 + t.millitm);
#else
struct timeval t;
gettimeofday(&t, NULL);
return t.tv_sec*1000 + t.tv_usec/1000;
struct timeval t;
gettimeofday(&t, NULL);
return t.tv_sec * 1000 + t.tv_usec / 1000;
#endif
}
/// cpu_count() tries to detect the number of CPU cores.
#if !defined(_MSC_VER)
# if defined(_SC_NPROCESSORS_ONLN)
int cpu_count() {
return Min(sysconf(_SC_NPROCESSORS_ONLN), MAX_THREADS);
}
# elif defined(__hpux)
int cpu_count() {
struct pst_dynamic psd;
if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1)
return 1;
return Min(psd.psd_proc_cnt, MAX_THREADS);
}
# else
int cpu_count() {
return 1;
}
# endif
#else
/// cpu_count() tries to detect the number of CPU cores
int cpu_count() {
#if defined(_MSC_VER)
SYSTEM_INFO s;
GetSystemInfo(&s);
return Min(s.dwNumberOfProcessors, MAX_THREADS);
}
#else
# if defined(_SC_NPROCESSORS_ONLN)
return Min(sysconf(_SC_NPROCESSORS_ONLN), MAX_THREADS);
# elif defined(__hpux)
struct pst_dynamic psd;
if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1)
return 1;
return Min(psd.psd_proc_cnt, MAX_THREADS);
# else
return 1;
# endif
#endif
}
/// Check for console input. Original code from Beowulf and Olithink
/// Check for console input. Original code from Beowulf, Olithink and Greko
#ifndef _WIN32
int data_available()
{
int input_available() {
fd_set readfds;
struct timeval timeout;
@@ -225,43 +193,54 @@ int data_available()
#else
int data_available()
{
static HANDLE inh = NULL;
static bool usePipe;
INPUT_RECORD rec[256];
DWORD dw, recCnt;
int input_available() {
if (!inh)
{
inh = GetStdHandle(STD_INPUT_HANDLE);
usePipe = !GetConsoleMode(inh, &dw);
if (!usePipe)
{
SetConsoleMode(inh, dw & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
FlushConsoleInputBuffer(inh);
}
}
static HANDLE inh = NULL;
static bool usePipe = false;
INPUT_RECORD rec[256];
DWORD nchars, recCnt;
// If we're running under XBoard then we can't use PeekConsoleInput() as
// the input commands are sent to us directly over the internal pipe.
if (usePipe)
return PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL) ? dw : 1;
if (!inh)
{
inh = GetStdHandle(STD_INPUT_HANDLE);
if (GetConsoleMode(inh, &nchars))
{
SetConsoleMode(inh, nchars & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
FlushConsoleInputBuffer(inh);
} else
usePipe = true;
}
// Count the number of unread input records, including keyboard,
// mouse, and window-resizing input records.
GetNumberOfConsoleInputEvents(inh, &dw);
// When using Standard C input functions, also check if there
// is anything in the buffer. After a call to such functions,
// the input waiting in the pipe will be copied to the buffer,
// and the call to PeekNamedPipe can indicate no input available.
// Setting stdin to unbuffered was not enough. [from Greko]
if (stdin->_cnt > 0)
return 1;
// Read data from console without removing it from the buffer
if (dw <= 0 || !PeekConsoleInput(inh, rec, Min(dw, 256), &recCnt))
return 0;
// When running under a GUI the input commands are sent to us
// directly over the internal pipe. If PeekNamedPipe() returns 0
// then something went wrong. Probably the parent program exited.
// Returning 1 will make the next call to the input function
// return EOF, where this should be catched then.
if (usePipe)
return PeekNamedPipe(inh, NULL, 0, NULL, &nchars, NULL) ? nchars : 1;
// Search for at least one keyboard event
for (DWORD i = 0; i < recCnt; i++)
if (rec[i].EventType == KEY_EVENT)
return 1;
// Count the number of unread input records, including keyboard,
// mouse, and window-resizing input records.
GetNumberOfConsoleInputEvents(inh, &nchars);
return 0;
// Read data from console without removing it from the buffer
if (nchars <= 0 || !PeekConsoleInput(inh, rec, Min(nchars, 256), &recCnt))
return 0;
// Search for at least one keyboard event
for (DWORD i = 0; i < recCnt; i++)
if (rec[i].EventType == KEY_EVENT)
return 1;
return 0;
}
#endif
@@ -269,9 +248,11 @@ int data_available()
/// prefetch() preloads the given address in L1/L2 cache. This is a non
/// blocking function and do not stalls the CPU waiting for data to be
/// loaded from RAM, that can be very slow.
/// loaded from memory, that can be quite slow.
#if defined(NO_PREFETCH)
void prefetch(char*) {}
#else
void prefetch(char* addr) {
@@ -287,4 +268,3 @@ void prefetch(char* addr) {
}
#endif
+2 -30
View File
@@ -17,46 +17,18 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MISC_H_INCLUDED)
#define MISC_H_INCLUDED
////
//// Includes
////
#include <fstream>
#include <string>
#include "types.h"
////
//// Macros
////
#define Min(x, y) (((x) < (y))? (x) : (y))
#define Max(x, y) (((x) < (y))? (y) : (x))
////
//// Prototypes
////
extern const std::string engine_name();
extern const std::string engine_authors();
extern int get_system_time();
extern int cpu_count();
extern int data_available();
extern int input_available();
extern void prefetch(char* addr);
extern void prefetchPawn(Key, int);
////
//// Debug
////
extern bool dbg_show_mean;
extern bool dbg_show_hit_rate;
extern void dbg_hit_on(bool b);
extern void dbg_hit_on_c(bool c, bool b);
+208 -102
View File
@@ -17,90 +17,21 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include <cctype>
#include <cstring>
#include <iomanip>
#include <string>
#include <sstream>
#include "move.h"
#include "piece.h"
#include "position.h"
#include "movegen.h"
#include "search.h"
using std::string;
////
//// Functions
////
/// move_from_uci() takes a position and a string as input, and attempts to
/// convert the string to a move, using simple coordinate notation (g1f3,
/// a7a8q, etc.). In order to correctly parse en passant captures and castling
/// moves, we need the position. This function is not robust, and expects that
/// the input move is legal and correctly formatted.
Move move_from_uci(const Position& pos, const std::string& str) {
Square from, to;
Piece piece;
Color us = pos.side_to_move();
if (str.length() < 4)
return MOVE_NONE;
// Read the from and to squares
from = make_square(file_from_char(str[0]), rank_from_char(str[1]));
to = make_square(file_from_char(str[2]), rank_from_char(str[3]));
// Find the moving piece
piece = pos.piece_on(from);
// If the string has more than 4 characters, try to interpret the 5th
// character as a promotion.
if (str.length() > 4 && piece == piece_of_color_and_type(us, PAWN))
{
switch (tolower(str[4])) {
case 'n':
return make_promotion_move(from, to, KNIGHT);
case 'b':
return make_promotion_move(from, to, BISHOP);
case 'r':
return make_promotion_move(from, to, ROOK);
case 'q':
return make_promotion_move(from, to, QUEEN);
}
}
// En passant move? We assume that a pawn move is an en passant move
// if the destination square is epSquare.
if (to == pos.ep_square() && piece == piece_of_color_and_type(us, PAWN))
return make_ep_move(from, to);
// Is this a castling move? A king move is assumed to be a castling move
// if the destination square is occupied by a friendly rook, or if the
// distance between the source and destination squares is more than 1.
if (piece == piece_of_color_and_type(us, KING))
{
if (pos.piece_on(to) == piece_of_color_and_type(us, ROOK))
return make_castle_move(from, to);
if (square_distance(from, to) > 1)
{
// This is a castling move, but we have to translate it to the
// internal "king captures rook" representation.
SquareDelta delta = (to > from ? DELTA_E : DELTA_W);
Square s = from;
do s += delta;
while ( pos.piece_on(s) != piece_of_color_and_type(us, ROOK)
&& relative_rank(us, s) == RANK_1);
return relative_rank(us, s) == RANK_1 ? make_castle_move(from, s) : MOVE_NONE;
}
}
return make_move(from, to);
namespace {
const string time_string(int milliseconds);
const string score_string(Value v);
}
@@ -109,44 +40,219 @@ Move move_from_uci(const Position& pos, const std::string& str) {
/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
/// Chess960 mode.
const std::string move_to_uci(Move move, bool chess960) {
const string move_to_uci(Move m, bool chess960) {
std::string str;
Square from = move_from(move);
Square to = move_to(move);
Square from = move_from(m);
Square to = move_to(m);
string promotion;
if (move == MOVE_NONE)
str = "(none)";
else if (move == MOVE_NULL)
str = "0000";
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "0000";
if (move_is_short_castle(m) && !chess960)
return from == SQ_E1 ? "e1g1" : "e8g8";
if (move_is_long_castle(m) && !chess960)
return from == SQ_E1 ? "e1c1" : "e8c8";
if (move_is_promotion(m))
promotion = char(tolower(piece_type_to_char(move_promotion_piece(m))));
return square_to_string(from) + square_to_string(to) + promotion;
}
/// move_from_uci() takes a position and a string representing a move in
/// simple coordinate notation and returns an equivalent Move if any.
/// Moves are guaranteed to be legal.
Move move_from_uci(const Position& pos, const string& str) {
MoveStack mlist[MAX_MOVES];
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
for (MoveStack* cur = mlist; cur != last; cur++)
if (str == move_to_uci(cur->move, pos.is_chess960()))
return cur->move;
return MOVE_NONE;
}
/// move_to_san() takes a position and a move as input, where it is assumed
/// that the move is a legal move from the position. The return value is
/// a string containing the move in short algebraic notation.
const string move_to_san(Position& pos, Move m) {
assert(pos.is_ok());
assert(move_is_ok(m));
MoveStack mlist[MAX_MOVES];
Square from = move_from(m);
Square to = move_to(m);
PieceType pt = pos.type_of_piece_on(from);
string san;
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "(null)";
if (move_is_long_castle(m))
san = "O-O-O";
else if (move_is_short_castle(m))
san = "O-O";
else
{
if (move_is_short_castle(move) && !chess960)
return (from == SQ_E1 ? "e1g1" : "e8g8");
if (pt != PAWN)
{
san = piece_type_to_char(pt);
if (move_is_long_castle(move) && !chess960)
return (from == SQ_E1 ? "e1c1" : "e8c8");
// Collect all legal moves of piece type 'pt' with destination 'to'
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
int f = 0, r = 0;
str = square_to_string(from) + square_to_string(to);
if (move_is_promotion(move))
str += char(tolower(piece_type_to_char(move_promotion_piece(move))));
for (MoveStack* cur = mlist; cur != last; cur++)
if ( move_to(cur->move) == to
&& pos.type_of_piece_on(move_from(cur->move)) == pt)
{
if (square_file(move_from(cur->move)) == square_file(from))
f++;
if (square_rank(move_from(cur->move)) == square_rank(from))
r++;
}
assert(f > 0 && r > 0);
// Disambiguation if we have more then one piece with destination 'to'
if (f == 1 && r > 1)
san += file_to_char(square_file(from));
else if (f > 1 && r == 1)
san += rank_to_char(square_rank(from));
else if (f > 1 && r > 1)
san += square_to_string(from);
}
if (pos.move_is_capture(m))
{
if (pt == PAWN)
san += file_to_char(square_file(from));
san += 'x';
}
san += square_to_string(to);
if (move_is_promotion(m))
{
san += '=';
san += piece_type_to_char(move_promotion_piece(m));
}
}
return str;
// The move gives check? We don't use pos.move_gives_check() here
// because we need to test for a mate after the move is done.
StateInfo st;
pos.do_move(m, st);
if (pos.in_check())
san += pos.is_mate() ? "#" : "+";
pos.undo_move(m);
return san;
}
/// Overload the << operator, to make it easier to print moves.
/// pretty_pv() creates a human-readable string from a position and a PV.
/// It is used to write search information to the log file (which is created
/// when the UCI parameter "Use Search Log" is "true").
std::ostream& operator << (std::ostream& os, Move m) {
const string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]) {
bool chess960 = (os.iword(0) != 0); // See set960()
return os << move_to_uci(m, chess960);
const int64_t K = 1000;
const int64_t M = 1000000;
const int startColumn = 28;
const size_t maxLength = 80 - startColumn;
const string lf = string("\n") + string(startColumn, ' ');
StateInfo state[PLY_MAX_PLUS_2], *st = state;
Move* m = pv;
string san;
std::stringstream s;
size_t length = 0;
// First print depth, score, time and searched nodes...
s << std::setw(2) << depth
<< std::setw(8) << score_string(score)
<< std::setw(8) << time_string(time);
if (pos.nodes_searched() < M)
s << std::setw(8) << pos.nodes_searched() / 1 << " ";
else if (pos.nodes_searched() < K * M)
s << std::setw(7) << pos.nodes_searched() / K << "K ";
else
s << std::setw(7) << pos.nodes_searched() / M << "M ";
// ...then print the full PV line in short algebraic notation
while (*m != MOVE_NONE)
{
san = move_to_san(pos, *m);
length += san.length() + 1;
if (length > maxLength)
{
length = san.length() + 1;
s << lf;
}
s << san << ' ';
pos.do_move(*m++, *st++);
}
// Restore original position before to leave
while (m != pv) pos.undo_move(*--m);
return s.str();
}
/// move_is_ok(), for debugging.
namespace {
bool move_is_ok(Move m) {
const string time_string(int millisecs) {
return square_is_ok(move_from(m)) && square_is_ok(move_to(m));
const int MSecMinute = 1000 * 60;
const int MSecHour = 1000 * 60 * 60;
int hours = millisecs / MSecHour;
int minutes = (millisecs % MSecHour) / MSecMinute;
int seconds = ((millisecs % MSecHour) % MSecMinute) / 1000;
std::stringstream s;
if (hours)
s << hours << ':';
s << std::setfill('0') << std::setw(2) << minutes << ':' << std::setw(2) << seconds;
return s.str();
}
const string score_string(Value v) {
std::stringstream s;
if (v >= VALUE_MATE - 200)
s << "#" << (VALUE_MATE - v + 1) / 2;
else if (v <= -VALUE_MATE + 200)
s << "-#" << (VALUE_MATE + v) / 2;
else
s << std::setprecision(2) << std::fixed << std::showpos << float(v) / PawnValueMidgame;
return s.str();
}
}
+28 -44
View File
@@ -17,36 +17,23 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MOVE_H_INCLUDED)
#define MOVE_H_INCLUDED
////
//// Includes
////
#include <iostream>
#include <string>
#include "misc.h"
#include "piece.h"
#include "square.h"
#include "types.h"
// Maximum number of allowed moves per position
const int MOVES_MAX = 256;
const int MAX_MOVES = 256;
////
//// Types
////
class Position;
/// A move needs 17 bits to be stored
/// A move needs 16 bits to be stored
///
/// bit 0- 5: destination square (from 0 to 63)
/// bit 6-11: origin square (from 0 to 63)
/// bit 12-14: promotion piece type
/// bit 15: en passant flag
/// bit 16: castle flag
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castle (3)
///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in
/// because in any normal move destination square is always different
@@ -141,9 +128,6 @@ inline T pick_best(T* curMove, T* lastMove)
return bestMove;
}
////
//// Inline functions
////
inline Square move_from(Move m) {
return Square((int(m) >> 6) & 0x3F);
@@ -153,24 +137,20 @@ inline Square move_to(Move m) {
return Square(m & 0x3F);
}
inline PieceType move_promotion_piece(Move m) {
return PieceType((int(m) >> 12) & 7);
inline bool move_is_special(Move m) {
return m & (3 << 14);
}
inline int move_is_special(Move m) {
return m & (0x1F << 12);
}
inline int move_is_promotion(Move m) {
return m & (7 << 12);
inline bool move_is_promotion(Move m) {
return (m & (3 << 14)) == (1 << 14);
}
inline int move_is_ep(Move m) {
return m & (1 << 15);
return (m & (3 << 14)) == (2 << 14);
}
inline int move_is_castle(Move m) {
return m & (1 << 16);
return (m & (3 << 14)) == (3 << 14);
}
inline bool move_is_short_castle(Move m) {
@@ -181,31 +161,35 @@ inline bool move_is_long_castle(Move m) {
return move_is_castle(m) && (move_to(m) < move_from(m));
}
inline Move make_promotion_move(Square from, Square to, PieceType promotion) {
return Move(int(to) | (int(from) << 6) | (int(promotion) << 12));
inline PieceType move_promotion_piece(Move m) {
return move_is_promotion(m) ? PieceType(((int(m) >> 12) & 3) + 2) : PIECE_TYPE_NONE;
}
inline Move make_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6));
}
inline Move make_castle_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (1 << 16));
inline Move make_promotion_move(Square from, Square to, PieceType promotion) {
return Move(int(to) | (int(from) << 6) | ((int(promotion) - 2) << 12) | (1 << 14));
}
inline Move make_ep_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (1 << 15));
return Move(int(to) | (int(from) << 6) | (2 << 14));
}
inline Move make_castle_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (3 << 14));
}
////
//// Prototypes
////
inline bool move_is_ok(Move m) {
return move_from(m) != move_to(m); // Catches also MOVE_NONE
}
class Position;
extern std::ostream& operator<<(std::ostream& os, Move m);
extern Move move_from_uci(const Position& pos, const std::string &str);
extern const std::string move_to_uci(Move m, bool chess960);
extern bool move_is_ok(Move m);
extern Move move_from_uci(const Position& pos, const std::string& str);
extern const std::string move_to_san(Position& pos, Move m);
extern const std::string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]);
#endif // !defined(MOVE_H_INCLUDED)
+197 -354
View File
@@ -17,11 +17,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include "bitcount.h"
@@ -34,10 +29,6 @@
// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_MOVES_D(b, d) while (b) { to = pop_1st_bit(&b); (*mlist++).move = make_move(to + (d), to); }
////
//// Local definitions
////
namespace {
enum CastlingSide {
@@ -45,141 +36,180 @@ namespace {
QUEEN_SIDE
};
enum MoveType {
CAPTURE,
NON_CAPTURE,
CHECK,
EVASION
};
template<CastlingSide>
MoveStack* generate_castle_moves(const Position&, MoveStack*, Color us);
// Helper templates
template<CastlingSide Side>
MoveStack* generate_castle_moves(const Position&, MoveStack*);
template<Color Us, MoveType Type>
template<Color, MoveType>
MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square);
// Template generate_piece_moves (captures and non-captures) with specializations and overloads
template<PieceType>
MoveStack* generate_piece_moves(const Position&, MoveStack*, Color, Bitboard);
template<PieceType Pt>
inline MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) {
assert(Pt != QUEEN);
Bitboard b = pos.attacks_from<Pt>(from) & pos.empty_squares();
if (Pt == KING)
{
Square ksq = pos.king_square(opposite_color(pos.side_to_move()));
b &= ~QueenPseudoAttacks[ksq];
}
SERIALIZE_MOVES(b);
return mlist;
}
template<PieceType Pt>
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
Bitboard dc, Square ksq) {
assert(Pt != KING);
Bitboard checkSqs, b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Pt);
if ((from = *ptr++) == SQ_NONE)
return mlist;
checkSqs = pos.attacks_from<Pt>(ksq) & pos.empty_squares();
do
{
if ( (Pt == QUEEN && !(QueenPseudoAttacks[from] & checkSqs))
|| (Pt == ROOK && !(RookPseudoAttacks[from] & checkSqs))
|| (Pt == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
continue;
if (dc && bit_is_set(dc, from))
continue;
b = pos.attacks_from<Pt>(from) & checkSqs;
SERIALIZE_MOVES(b);
} while ((from = *ptr++) != SQ_NONE);
return mlist;
}
template<>
MoveStack* generate_piece_moves<KING>(const Position&, MoveStack*, Color, Bitboard);
FORCE_INLINE MoveStack* generate_direct_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) {
template<PieceType Piece, MoveType Type>
inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) {
return (us == WHITE ? generate_pawn_moves<WHITE, MV_CHECK>(p, m, dc, ksq)
: generate_pawn_moves<BLACK, MV_CHECK>(p, m, dc, ksq));
}
assert(Piece == PAWN);
assert(Type == CAPTURE || Type == NON_CAPTURE || Type == EVASION);
template<PieceType Pt, MoveType Type>
FORCE_INLINE MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) {
assert(Pt == PAWN);
assert(Type == MV_CAPTURE || Type == MV_NON_CAPTURE || Type == MV_EVASION);
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE)
: generate_pawn_moves<BLACK, Type>(p, m, t, SQ_NONE));
}
// Templates for non-capture checks generation
template<PieceType Pt>
FORCE_INLINE MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
template<PieceType Piece>
MoveStack* generate_discovered_checks(const Position&, MoveStack*, Square);
Bitboard b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Pt);
template<PieceType>
MoveStack* generate_direct_checks(const Position&, MoveStack*, Color, Bitboard, Square);
if (*ptr != SQ_NONE)
{
do {
from = *ptr;
b = pos.attacks_from<Pt>(from) & target;
SERIALIZE_MOVES(b);
} while (*++ptr != SQ_NONE);
}
return mlist;
}
template<>
inline MoveStack* generate_direct_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) {
FORCE_INLINE MoveStack* generate_piece_moves<KING>(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
return (us == WHITE ? generate_pawn_moves<WHITE, CHECK>(p, m, dc, ksq)
: generate_pawn_moves<BLACK, CHECK>(p, m, dc, ksq));
Bitboard b;
Square from = pos.king_square(us);
b = pos.attacks_from<KING>(from) & target;
SERIALIZE_MOVES(b);
return mlist;
}
}
////
//// Functions
////
/// generate_captures() generates all pseudo-legal captures and queen
/// generate<MV_CAPTURE> generates all pseudo-legal captures and queen
/// promotions. Returns a pointer to the end of the move list.
MoveStack* generate_captures(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.is_check());
Color us = pos.side_to_move();
Bitboard target = pos.pieces_of_color(opposite_color(us));
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<PAWN, CAPTURE>(pos, mlist, us, target);
return generate_piece_moves<KING>(pos, mlist, us, target);
}
/// generate_noncaptures() generates all pseudo-legal non-captures and
///
/// generate<MV_NON_CAPTURE> generates all pseudo-legal non-captures and
/// underpromotions. Returns a pointer to the end of the move list.
MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.is_check());
Color us = pos.side_to_move();
Bitboard target = pos.empty_squares();
mlist = generate_piece_moves<PAWN, NON_CAPTURE>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
mlist = generate_piece_moves<KING>(pos, mlist, us, target);
mlist = generate_castle_moves<KING_SIDE>(pos, mlist);
return generate_castle_moves<QUEEN_SIDE>(pos, mlist);
}
/// generate_non_evasions() generates all pseudo-legal captures and
///
/// generate<MV_NON_EVASION> generates all pseudo-legal captures and
/// non-captures. Returns a pointer to the end of the move list.
MoveStack* generate_non_evasions(const Position& pos, MoveStack* mlist) {
template<MoveType Type>
MoveStack* generate(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.is_check());
assert(!pos.in_check());
Color us = pos.side_to_move();
Bitboard target = pos.pieces_of_color(opposite_color(us));
Bitboard target;
mlist = generate_piece_moves<PAWN, CAPTURE>(pos, mlist, us, target);
mlist = generate_piece_moves<PAWN, NON_CAPTURE>(pos, mlist, us, pos.empty_squares());
if (Type == MV_CAPTURE || Type == MV_NON_EVASION)
target = pos.pieces_of_color(opposite_color(us));
else if (Type == MV_NON_CAPTURE)
target = pos.empty_squares();
else
assert(false);
target |= pos.empty_squares();
if (Type == MV_NON_EVASION)
{
mlist = generate_piece_moves<PAWN, MV_CAPTURE>(pos, mlist, us, target);
mlist = generate_piece_moves<PAWN, MV_NON_CAPTURE>(pos, mlist, us, pos.empty_squares());
target |= pos.empty_squares();
}
else
mlist = generate_piece_moves<PAWN, Type>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
mlist = generate_piece_moves<KING>(pos, mlist, us, target);
mlist = generate_castle_moves<KING_SIDE>(pos, mlist);
return generate_castle_moves<QUEEN_SIDE>(pos, mlist);
if (Type != MV_CAPTURE)
{
if (pos.can_castle_kingside(us))
mlist = generate_castle_moves<KING_SIDE>(pos, mlist, us);
if (pos.can_castle_queenside(us))
mlist = generate_castle_moves<QUEEN_SIDE>(pos, mlist, us);
}
return mlist;
}
// Explicit template instantiations
template MoveStack* generate<MV_CAPTURE>(const Position& pos, MoveStack* mlist);
template MoveStack* generate<MV_NON_CAPTURE>(const Position& pos, MoveStack* mlist);
template MoveStack* generate<MV_NON_EVASION>(const Position& pos, MoveStack* mlist);
/// generate_non_capture_checks() generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list.
MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist) {
template<>
MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.is_check());
assert(!pos.in_check());
Bitboard b, dc;
Square from;
Color us = pos.side_to_move();
Square ksq = pos.king_square(opposite_color(us));
assert(pos.piece_on(ksq) == piece_of_color_and_type(opposite_color(us), KING));
assert(pos.piece_on(ksq) == make_piece(opposite_color(us), KING));
// Discovered non-capture checks
b = dc = pos.discovered_check_candidates(us);
@@ -209,11 +239,11 @@ MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist) {
/// generate_evasions() generates all pseudo-legal check evasions when
/// the side to move is in check. Returns a pointer to the end of the move list.
MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) {
template<>
MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(pos.is_check());
assert(pos.in_check());
Bitboard b, target;
Square from, checksq;
@@ -223,7 +253,7 @@ MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) {
Bitboard checkers = pos.checkers();
Bitboard sliderAttacks = EmptyBoardBB;
assert(pos.piece_on(ksq) == piece_of_color_and_type(us, KING));
assert(pos.piece_on(ksq) == make_piece(us, KING));
assert(checkers);
// Find squares attacked by slider checkers, we will remove
@@ -266,7 +296,7 @@ MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) {
// checker piece is possible.
target = squares_between(checksq, ksq) | checkers;
mlist = generate_piece_moves<PAWN, EVASION>(pos, mlist, us, target);
mlist = generate_piece_moves<PAWN, MV_EVASION>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
@@ -274,177 +304,41 @@ MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) {
}
/// generate_moves() computes a complete list of legal or pseudo-legal moves in
/// the current position. This function is not very fast, and should be used
/// only in non time-critical paths.
/// generate<MV_LEGAL / MV_PSEUDO_LEGAL> computes a complete list of legal
/// or pseudo-legal moves in the current position.
template<>
MoveStack* generate<MV_PSEUDO_LEGAL>(const Position& pos, MoveStack* mlist) {
MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLegal) {
assert(pos.is_ok());
return pos.in_check() ? generate<MV_EVASION>(pos, mlist)
: generate<MV_NON_EVASION>(pos, mlist);
}
template<>
MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
MoveStack *last, *cur = mlist;
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
// Generate pseudo-legal moves
last = pos.is_check() ? generate_evasions(pos, mlist)
: generate_non_evasions(pos, mlist);
if (pseudoLegal)
return last;
last = generate<MV_PSEUDO_LEGAL>(pos, mlist);
// Remove illegal moves from the list
while (cur != last)
if (pos.pl_move_is_legal(cur->move, pinned))
cur++;
else
if (!pos.pl_move_is_legal(cur->move, pinned))
cur->move = (--last)->move;
else
cur++;
return last;
}
/// move_is_legal() takes a position and a (not necessarily pseudo-legal)
/// move and tests whether the move is legal. This version is not very fast
/// and should be used only in non time-critical paths.
bool move_is_legal(const Position& pos, const Move m) {
MoveStack mlist[MOVES_MAX];
MoveStack *cur, *last = generate_moves(pos, mlist, true);
for (cur = mlist; cur != last; cur++)
if (cur->move == m)
return pos.pl_move_is_legal(m, pos.pinned_pieces(pos.side_to_move()));
return false;
}
/// Fast version of move_is_legal() that takes a position a move and a
/// bitboard of pinned pieces as input, and tests whether the move is legal.
bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
assert(pos.is_ok());
assert(move_is_ok(m));
assert(pinned == pos.pinned_pieces(pos.side_to_move()));
Color us = pos.side_to_move();
Color them = opposite_color(us);
Square from = move_from(m);
Square to = move_to(m);
Piece pc = pos.piece_on(from);
// Use a slower but simpler function for uncommon cases
if (move_is_special(m))
return move_is_legal(pos, m);
// If the from square is not occupied by a piece belonging to the side to
// move, the move is obviously not legal.
if (color_of_piece(pc) != us)
return false;
// The destination square cannot be occupied by a friendly piece
if (pos.color_of_piece_on(to) == us)
return false;
// Handle the special case of a pawn move
if (type_of_piece(pc) == PAWN)
{
// Move direction must be compatible with pawn color
int direction = to - from;
if ((us == WHITE) != (direction > 0))
return false;
// We have already handled promotion moves, so destination
// cannot be on the 8/1th rank.
if (square_rank(to) == RANK_8 || square_rank(to) == RANK_1)
return false;
// Proceed according to the square delta between the origin and
// destination squares.
switch (direction)
{
case DELTA_NW:
case DELTA_NE:
case DELTA_SW:
case DELTA_SE:
// Capture. The destination square must be occupied by an enemy
// piece (en passant captures was handled earlier).
if (pos.color_of_piece_on(to) != them)
return false;
break;
case DELTA_N:
case DELTA_S:
// Pawn push. The destination square must be empty.
if (!pos.square_is_empty(to))
return false;
break;
case DELTA_NN:
// Double white pawn push. The destination square must be on the fourth
// rank, and both the destination square and the square between the
// source and destination squares must be empty.
if ( square_rank(to) != RANK_4
|| !pos.square_is_empty(to)
|| !pos.square_is_empty(from + DELTA_N))
return false;
break;
case DELTA_SS:
// Double black pawn push. The destination square must be on the fifth
// rank, and both the destination square and the square between the
// source and destination squares must be empty.
if ( square_rank(to) != RANK_5
|| !pos.square_is_empty(to)
|| !pos.square_is_empty(from + DELTA_S))
return false;
break;
default:
return false;
}
}
else if (!bit_is_set(pos.attacks_from(pc, from), to))
return false;
// The move is pseudo-legal, check if it is also legal
return pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned);
}
namespace {
template<PieceType Piece>
MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Bitboard b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Piece);
if (*ptr != SQ_NONE)
{
do {
from = *ptr;
b = pos.attacks_from<Piece>(from) & target;
SERIALIZE_MOVES(b);
} while (*++ptr != SQ_NONE);
}
return mlist;
}
template<>
MoveStack* generate_piece_moves<KING>(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Bitboard b;
Square from = pos.king_square(us);
b = pos.attacks_from<KING>(from) & target;
SERIALIZE_MOVES(b);
return mlist;
}
template<SquareDelta Delta>
template<Square Delta>
inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8 : Delta == DELTA_S ? p >> 8 :
@@ -452,7 +346,7 @@ namespace {
Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p;
}
template<MoveType Type, SquareDelta Delta>
template<MoveType Type, Square Delta>
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
@@ -466,7 +360,7 @@ namespace {
return mlist;
}
template<Color Us, MoveType Type, SquareDelta Delta>
template<Color Us, MoveType Type, Square Delta>
inline MoveStack* generate_promotions(const Position& pos, MoveStack* mlist, Bitboard pawnsOn7, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
@@ -484,10 +378,10 @@ namespace {
{
to = pop_1st_bit(&b);
if (Type == CAPTURE || Type == EVASION)
if (Type == MV_CAPTURE || Type == MV_EVASION)
(*mlist++).move = make_promotion_move(to - Delta, to, QUEEN);
if (Type == NON_CAPTURE || Type == EVASION)
if (Type == MV_NON_CAPTURE || Type == MV_EVASION)
{
(*mlist++).move = make_promotion_move(to - Delta, to, ROOK);
(*mlist++).move = make_promotion_move(to - Delta, to, BISHOP);
@@ -496,7 +390,7 @@ namespace {
// This is the only possible under promotion that can give a check
// not already included in the queen-promotion.
if ( Type == CHECK
if ( Type == MV_CHECK
&& bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(opposite_color(Us))))
(*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT);
else (void)pos; // Silence a warning under MSVC
@@ -509,27 +403,27 @@ namespace {
// Calculate our parametrized parameters at compile time, named
// according to the point of view of white side.
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S);
const SquareDelta TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S);
const Square TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
const Square TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
Square to;
Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares;
Bitboard pawns = pos.pieces(PAWN, Us);
Bitboard pawnsOn7 = pawns & TRank7BB;
Bitboard enemyPieces = (Type == CAPTURE ? target : pos.pieces_of_color(Them));
Bitboard enemyPieces = (Type == MV_CAPTURE ? target : pos.pieces_of_color(Them));
// Pre-calculate pawn pushes before changing emptySquares definition
if (Type != CAPTURE)
if (Type != MV_CAPTURE)
{
emptySquares = (Type == NON_CAPTURE ? target : pos.empty_squares());
emptySquares = (Type == MV_NON_CAPTURE ? target : pos.empty_squares());
pawnPushes = move_pawns<TDELTA_N>(pawns & ~TRank7BB) & emptySquares;
}
if (Type == EVASION)
if (Type == MV_EVASION)
{
emptySquares &= target; // Only blocking squares
enemyPieces &= target; // Capture only the checker piece
@@ -538,7 +432,7 @@ namespace {
// Promotions and underpromotions
if (pawnsOn7)
{
if (Type == CAPTURE)
if (Type == MV_CAPTURE)
emptySquares = pos.empty_squares();
pawns &= ~TRank7BB;
@@ -548,21 +442,21 @@ namespace {
}
// Standard captures
if (Type == CAPTURE || Type == EVASION)
if (Type == MV_CAPTURE || Type == MV_EVASION)
{
mlist = generate_pawn_captures<Type, TDELTA_NE>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<Type, TDELTA_NW>(mlist, pawns, enemyPieces);
}
// Single and double pawn pushes
if (Type != CAPTURE)
if (Type != MV_CAPTURE)
{
b1 = pawnPushes & emptySquares;
b2 = move_pawns<TDELTA_N>(pawnPushes & TRank3BB) & emptySquares;
if (Type == CHECK)
if (Type == MV_CHECK)
{
// Condider only pawn moves which give direct checks
// Consider only pawn moves which give direct checks
b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them);
@@ -583,7 +477,7 @@ namespace {
}
// En passant captures
if ((Type == CAPTURE || Type == EVASION) && pos.ep_square() != SQ_NONE)
if ((Type == MV_CAPTURE || Type == MV_EVASION) && pos.ep_square() != SQ_NONE)
{
assert(Us != WHITE || square_rank(pos.ep_square()) == RANK_6);
assert(Us != BLACK || square_rank(pos.ep_square()) == RANK_3);
@@ -591,7 +485,7 @@ namespace {
// An en passant capture can be an evasion only if the checking piece
// is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise.
if (Type == EVASION && !bit_is_set(target, pos.ep_square() - TDELTA_N))
if (Type == MV_EVASION && !bit_is_set(target, pos.ep_square() - TDELTA_N))
return mlist;
b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them);
@@ -607,93 +501,42 @@ namespace {
return mlist;
}
template<PieceType Piece>
MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) {
assert(Piece != QUEEN);
Bitboard b = pos.attacks_from<Piece>(from) & pos.empty_squares();
if (Piece == KING)
{
Square ksq = pos.king_square(opposite_color(pos.side_to_move()));
b &= ~QueenPseudoAttacks[ksq];
}
SERIALIZE_MOVES(b);
return mlist;
}
template<PieceType Piece>
MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
Bitboard dc, Square ksq) {
assert(Piece != KING);
Bitboard checkSqs, b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Piece);
if ((from = *ptr++) == SQ_NONE)
return mlist;
checkSqs = pos.attacks_from<Piece>(ksq) & pos.empty_squares();
do
{
if ( (Piece == QUEEN && !(QueenPseudoAttacks[from] & checkSqs))
|| (Piece == ROOK && !(RookPseudoAttacks[from] & checkSqs))
|| (Piece == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
continue;
if (dc && bit_is_set(dc, from))
continue;
b = pos.attacks_from<Piece>(from) & checkSqs;
SERIALIZE_MOVES(b);
} while ((from = *ptr++) != SQ_NONE);
return mlist;
}
template<CastlingSide Side>
MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist) {
MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) {
Color us = pos.side_to_move();
Color them = opposite_color(us);
Square ksq = pos.king_square(us);
if ( (Side == KING_SIDE && pos.can_castle_kingside(us))
||(Side == QUEEN_SIDE && pos.can_castle_queenside(us)))
{
Color them = opposite_color(us);
Square ksq = pos.king_square(us);
assert(pos.piece_on(ksq) == make_piece(us, KING));
assert(pos.piece_on(ksq) == piece_of_color_and_type(us, KING));
Square rsq = (Side == KING_SIDE ? pos.initial_kr_square(us) : pos.initial_qr_square(us));
Square s1 = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Square s2 = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
Square s;
bool illegal = false;
Square rsq = (Side == KING_SIDE ? pos.initial_kr_square(us) : pos.initial_qr_square(us));
Square s1 = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Square s2 = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
Square s;
bool illegal = false;
assert(pos.piece_on(rsq) == make_piece(us, ROOK));
assert(pos.piece_on(rsq) == piece_of_color_and_type(us, ROOK));
// It is a bit complicated to correctly handle Chess960
for (s = Min(ksq, s1); s <= Max(ksq, s1); s++)
if ( (s != ksq && s != rsq && pos.square_is_occupied(s))
||(pos.attackers_to(s) & pos.pieces_of_color(them)))
illegal = true;
for (s = Min(rsq, s2); s <= Max(rsq, s2); s++)
if (s != ksq && s != rsq && pos.square_is_occupied(s))
illegal = true;
if ( Side == QUEEN_SIDE
&& square_file(rsq) == FILE_B
&& ( pos.piece_on(relative_square(us, SQ_A1)) == piece_of_color_and_type(them, ROOK)
|| pos.piece_on(relative_square(us, SQ_A1)) == piece_of_color_and_type(them, QUEEN)))
// It is a bit complicated to correctly handle Chess960
for (s = Min(ksq, s1); s <= Max(ksq, s1); s++)
if ( (s != ksq && s != rsq && pos.square_is_occupied(s))
||(pos.attackers_to(s) & pos.pieces_of_color(them)))
illegal = true;
if (!illegal)
(*mlist++).move = make_castle_move(ksq, rsq);
}
for (s = Min(rsq, s2); s <= Max(rsq, s2); s++)
if (s != ksq && s != rsq && pos.square_is_occupied(s))
illegal = true;
if ( Side == QUEEN_SIDE
&& square_file(rsq) == FILE_B
&& ( pos.piece_on(relative_square(us, SQ_A1)) == make_piece(them, ROOK)
|| pos.piece_on(relative_square(us, SQ_A1)) == make_piece(them, QUEEN)))
illegal = true;
if (!illegal)
(*mlist++).move = make_castle_move(ksq, rsq);
return mlist;
}
}
} // namespace
+13 -18
View File
@@ -17,29 +17,24 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MOVEGEN_H_INCLUDED)
#define MOVEGEN_H_INCLUDED
////
//// Includes
////
#include "move.h"
#include "position.h"
enum MoveType {
MV_CAPTURE,
MV_NON_CAPTURE,
MV_CHECK,
MV_NON_CAPTURE_CHECK,
MV_EVASION,
MV_NON_EVASION,
MV_LEGAL,
MV_PSEUDO_LEGAL
};
////
//// Prototypes
////
extern MoveStack* generate_captures(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_evasions(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_non_evasions(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLegal = false);
extern bool move_is_legal(const Position& pos, const Move m, Bitboard pinned);
extern bool move_is_legal(const Position& pos, const Move m);
template<MoveType>
MoveStack* generate(const Position& pos, MoveStack* mlist);
#endif // !defined(MOVEGEN_H_INCLUDED)
+131 -138
View File
@@ -18,51 +18,35 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include "history.h"
#include "movegen.h"
#include "movepick.h"
#include "search.h"
#include "value.h"
////
//// Local definitions
////
#include "types.h"
namespace {
enum MovegenPhase {
PH_TT_MOVES, // Transposition table move and mate killer
PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0
PH_KILLERS, // Killer moves from the current ply
PH_NONCAPTURES, // Non-captures and underpromotions
PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < 0
PH_EVASIONS, // Check evasions
PH_QCAPTURES, // Captures in quiescence search
PH_QCHECKS, // Non-capture checks in quiescence search
PH_TT_MOVES, // Transposition table move and mate killer
PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0
PH_KILLERS, // Killer moves from the current ply
PH_NONCAPTURES, // Non-captures and underpromotions
PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < 0
PH_EVASIONS, // Check evasions
PH_QCAPTURES, // Captures in quiescence search
PH_QCHECKS, // Non-capture checks in quiescence search
PH_STOP
};
CACHE_LINE_ALIGNMENT
const uint8_t MainSearchPhaseTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
const uint8_t EvasionsPhaseTable[] = { PH_TT_MOVES, PH_EVASIONS, PH_STOP};
const uint8_t QsearchWithChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP};
const uint8_t QsearchWithoutChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP};
const uint8_t MainSearchTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP };
const uint8_t EvasionTable[] = { PH_TT_MOVES, PH_EVASIONS, PH_STOP };
const uint8_t QsearchWithChecksTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP };
const uint8_t QsearchWithoutChecksTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP };
}
////
//// Functions
////
/// Constructor for the MovePicker class. Apart from the position for which
/// it is asked to pick legal moves, MovePicker also wants some information
/// to help it to return the presumably good moves first, to decide which
@@ -75,35 +59,53 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
int searchTT = ttm;
ttMoves[0].move = ttm;
badCaptureThreshold = 0;
badCaptures = moves + MOVES_MAX;
badCaptures = moves + MAX_MOVES;
assert(d > DEPTH_ZERO);
pinned = p.pinned_pieces(pos.side_to_move());
if (ss && !p.is_check())
if (p.in_check())
{
ttMoves[1].move = killers[0].move = killers[1].move = MOVE_NONE;
phasePtr = EvasionTable;
}
else
{
ttMoves[1].move = (ss->mateKiller == ttm) ? MOVE_NONE : ss->mateKiller;
searchTT |= ttMoves[1].move;
killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1];
} else
ttMoves[1].move = killers[0].move = killers[1].move = MOVE_NONE;
if (p.is_check())
phasePtr = EvasionsPhaseTable;
else if (d > DEPTH_ZERO)
{
// Consider sligtly negative captures as good if at low
// depth and far from beta.
if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * ONE_PLY)
badCaptureThreshold = -PawnValueMidgame;
phasePtr = MainSearchPhaseTable;
phasePtr = MainSearchTable;
}
phasePtr += int(!searchTT) - 1;
go_next_phase();
}
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h)
: pos(p), H(h) {
int searchTT = ttm;
ttMoves[0].move = ttm;
ttMoves[1].move = MOVE_NONE;
assert(d <= DEPTH_ZERO);
pinned = p.pinned_pieces(pos.side_to_move());
if (p.in_check())
phasePtr = EvasionTable;
else if (d >= DEPTH_QS_CHECKS)
phasePtr = QsearchWithChecksPhaseTable;
phasePtr = QsearchWithChecksTable;
else
{
phasePtr = QsearchWithoutChecksPhaseTable;
phasePtr = QsearchWithoutChecksTable;
// Skip TT move if is not a capture or a promotion, this avoids
// qsearch tree explosion due to a possible perpetual check or
@@ -132,7 +134,7 @@ void MovePicker::go_next_phase() {
return;
case PH_GOOD_CAPTURES:
lastMove = generate_captures(pos, moves);
lastMove = generate<MV_CAPTURE>(pos, moves);
score_captures();
return;
@@ -142,7 +144,7 @@ void MovePicker::go_next_phase() {
return;
case PH_NONCAPTURES:
lastMove = generate_noncaptures(pos, moves);
lastMove = generate<MV_NON_CAPTURE>(pos, moves);
score_noncaptures();
sort_moves(moves, lastMove, &lastGoodNonCapture);
return;
@@ -151,26 +153,26 @@ void MovePicker::go_next_phase() {
// Bad captures SEE value is already calculated so just pick
// them in order to get SEE move ordering.
curMove = badCaptures;
lastMove = moves + MOVES_MAX;
lastMove = moves + MAX_MOVES;
return;
case PH_EVASIONS:
assert(pos.is_check());
lastMove = generate_evasions(pos, moves);
assert(pos.in_check());
lastMove = generate<MV_EVASION>(pos, moves);
score_evasions();
return;
case PH_QCAPTURES:
lastMove = generate_captures(pos, moves);
lastMove = generate<MV_CAPTURE>(pos, moves);
score_captures();
return;
case PH_QCHECKS:
lastMove = generate_non_capture_checks(pos, moves);
lastMove = generate<MV_NON_CAPTURE_CHECK>(pos, moves);
return;
case PH_STOP:
lastMove = curMove + 1; // Avoids another go_next_phase() call
lastMove = curMove + 1; // Avoid another go_next_phase() call
return;
default:
@@ -214,20 +216,15 @@ void MovePicker::score_captures() {
}
void MovePicker::score_noncaptures() {
// First score by history, when no history is available then use
// piece/square tables values. This seems to be better then a
// random choice when we don't have an history for any move.
Move m;
Piece piece;
Square from, to;
Square from;
for (MoveStack* cur = moves; cur != lastMove; cur++)
{
m = cur->move;
from = move_from(m);
to = move_to(m);
piece = pos.piece_on(from);
cur->score = H.value(piece, to) + H.gain(piece, to);
cur->score = H.value(pos.piece_on(from), move_to(m));
}
}
@@ -247,10 +244,10 @@ void MovePicker::score_evasions() {
{
m = cur->move;
if ((seeScore = pos.see_sign(m)) < 0)
cur->score = seeScore - HistoryMax; // Be sure are at the bottom
cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom
else if (pos.move_is_capture(m))
cur->score = pos.midgame_value_of_piece_on(move_to(m))
- pos.type_of_piece_on(move_from(m)) + HistoryMax;
- pos.type_of_piece_on(move_from(m)) + History::MaxValue;
else
cur->score = H.value(pos.piece_on(move_from(m)), move_to(m));
}
@@ -258,11 +255,10 @@ void MovePicker::score_evasions() {
/// MovePicker::get_next_move() is the most important method of the MovePicker
/// class. It returns a new legal move every time it is called, until there
/// are no more moves left.
/// It picks the move with the biggest score from a list of generated moves taking
/// care not to return the tt move if has already been searched previously.
/// Note that this function is not thread safe so should be lock protected by
/// caller when accessed through a shared MovePicker object.
/// are no more moves left. It picks the move with the biggest score from a list
/// of generated moves taking care not to return the tt move if has already been
/// searched previously. Note that this function is not thread safe so should be
/// lock protected by caller when accessed through a shared MovePicker object.
Move MovePicker::get_next_move() {
@@ -270,88 +266,85 @@ Move MovePicker::get_next_move() {
while (true)
{
while (curMove != lastMove)
{
switch (phase) {
while (curMove == lastMove)
go_next_phase();
case PH_TT_MOVES:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& move_is_legal(pos, move, pinned))
return move;
break;
switch (phase) {
case PH_GOOD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& pos.pl_move_is_legal(move, pinned))
{
// Check for a non negative SEE now
int seeValue = pos.see_sign(move);
if (seeValue >= badCaptureThreshold)
return move;
// Losing capture, move it to the tail of the array, note
// that move has now been already checked for legality.
(--badCaptures)->move = move;
badCaptures->score = seeValue;
}
break;
case PH_KILLERS:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& move_is_legal(pos, move, pinned)
&& move != ttMoves[0].move
&& move != ttMoves[1].move
&& !pos.move_is_capture(move))
return move;
break;
case PH_NONCAPTURES:
// Sort negative scored moves only when we get there
if (curMove == lastGoodNonCapture)
insertion_sort<MoveStack>(lastGoodNonCapture, lastMove);
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& move != killers[0].move
&& move != killers[1].move
&& pos.pl_move_is_legal(move, pinned))
return move;
break;
case PH_BAD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
case PH_TT_MOVES:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& pos.move_is_legal(move, pinned))
return move;
break;
case PH_EVASIONS:
case PH_QCAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
case PH_GOOD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& pos.pl_move_is_legal(move, pinned))
{
// Check for a non negative SEE now
int seeValue = pos.see_sign(move);
if (seeValue >= badCaptureThreshold)
return move;
break;
case PH_QCHECKS:
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
return move;
break;
case PH_STOP:
return MOVE_NONE;
default:
assert(false);
break;
// Losing capture, move it to the tail of the array, note
// that move has now been already checked for legality.
(--badCaptures)->move = move;
badCaptures->score = seeValue;
}
break;
case PH_KILLERS:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& pos.move_is_legal(move, pinned)
&& move != ttMoves[0].move
&& move != ttMoves[1].move
&& !pos.move_is_capture(move))
return move;
break;
case PH_NONCAPTURES:
// Sort negative scored moves only when we get there
if (curMove == lastGoodNonCapture)
insertion_sort<MoveStack>(lastGoodNonCapture, lastMove);
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& move != killers[0].move
&& move != killers[1].move
&& pos.pl_move_is_legal(move, pinned))
return move;
break;
case PH_BAD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
return move;
case PH_EVASIONS:
case PH_QCAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
return move;
break;
case PH_QCHECKS:
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
return move;
break;
case PH_STOP:
return MOVE_NONE;
default:
assert(false);
break;
}
go_next_phase();
}
}
+8 -32
View File
@@ -17,41 +17,32 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED
////
//// Includes
////
#include "depth.h"
#include "history.h"
#include "move.h"
#include "position.h"
////
//// Types
////
#include "types.h"
struct SearchStack;
/// MovePicker is a class which is used to pick one legal move at a time from
/// the current position. It is initialized with a Position object and a few
/// moves we have reason to believe are good. The most important method is
/// MovePicker::pick_next_move(), which returns a new legal move each time it
/// MovePicker::get_next_move(), which returns a new legal move each time it
/// is called, until there are no legal moves left, when MOVE_NONE is returned.
/// In order to improve the efficiency of the alpha beta algorithm, MovePicker
/// attempts to return the moves which are most likely to be strongest first.
/// attempts to return the moves which are most likely to get a cut-off first.
class MovePicker {
MovePicker& operator=(const MovePicker&); // silence a warning under MSVC
MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC
public:
MovePicker(const Position& p, Move ttm, Depth d, const History& h, SearchStack* ss = NULL, Value beta = -VALUE_INFINITE);
MovePicker(const Position&, Move, Depth, const History&, SearchStack*, Value);
MovePicker(const Position&, Move, Depth, const History&);
Move get_next_move();
int number_of_evasions() const;
private:
void score_captures();
@@ -66,22 +57,7 @@ private:
int badCaptureThreshold, phase;
const uint8_t* phasePtr;
MoveStack *curMove, *lastMove, *lastGoodNonCapture, *badCaptures;
MoveStack moves[MOVES_MAX];
MoveStack moves[MAX_MOVES];
};
////
//// Inline functions
////
/// MovePicker::number_of_evasions() simply returns the number of moves in
/// evasions phase. It is intended to be used in positions where the side to
/// move is in check, for detecting checkmates or situations where there is
/// only a single reply to check.
/// WARNING: It works as long as PH_EVASIONS is the _only_ phase for evasions.
inline int MovePicker::number_of_evasions() const {
return int(lastMove - moves);
}
#endif // !defined(MOVEPICK_H_INCLUDED)
+71 -66
View File
@@ -17,27 +17,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include <cstring>
#include "bitboard.h"
#include "bitcount.h"
#include "pawns.h"
#include "position.h"
////
//// Local definitions
////
namespace {
/// Constants and variables
#define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by opposed flag and file
@@ -73,39 +61,20 @@ namespace {
S(34,68), S(83,166), S(0, 0), S( 0, 0)
};
const Score PawnStructureWeight = S(233, 201);
#undef S
}
////
//// Functions
////
/// PawnInfoTable c'tor and d'tor instantiated one each thread
PawnInfoTable::PawnInfoTable() {
entries = new PawnInfo[PawnTableSize];
if (!entries)
{
std::cerr << "Failed to allocate " << (PawnTableSize * sizeof(PawnInfo))
<< " bytes for pawn hash table." << std::endl;
exit(EXIT_FAILURE);
inline Score apply_weight(Score v, Score w) {
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
}
memset(entries, 0, PawnTableSize * sizeof(PawnInfo));
}
PawnInfoTable::~PawnInfoTable() {
delete [] entries;
}
/// PawnInfoTable::get_pawn_info() takes a position object as input, computes
/// a PawnInfo object, and returns a pointer to it. The result is also stored
/// in a hash table, so we don't have to recompute everything when the same
/// in an hash table, so we don't have to recompute everything when the same
/// pawn structure occurs again.
PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
@@ -113,8 +82,7 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
assert(pos.is_ok());
Key key = pos.get_pawn_key();
unsigned index = unsigned(key & (PawnTableSize - 1));
PawnInfo* pi = entries + index;
PawnInfo* pi = probe(key);
// If pi->key matches the position's pawn hash key, it means that we
// have analysed this pawn structure before, and we can simply return
@@ -122,11 +90,11 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
if (pi->key == key)
return pi;
// Clear the PawnInfo object, and set the key
memset(pi, 0, sizeof(PawnInfo));
pi->halfOpenFiles[WHITE] = pi->halfOpenFiles[BLACK] = 0xFF;
pi->kingSquares[WHITE] = pi->kingSquares[BLACK] = SQ_NONE;
// Initialize PawnInfo entry
pi->key = key;
pi->passedPawns[WHITE] = pi->passedPawns[BLACK] = 0;
pi->kingSquares[WHITE] = pi->kingSquares[BLACK] = SQ_NONE;
pi->halfOpenFiles[WHITE] = pi->halfOpenFiles[BLACK] = 0xFF;
// Calculate pawn attacks
Bitboard wPawns = pos.pieces(PAWN, WHITE);
@@ -134,9 +102,12 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
pi->pawnAttacks[WHITE] = ((wPawns << 9) & ~FileABB) | ((wPawns << 7) & ~FileHBB);
pi->pawnAttacks[BLACK] = ((bPawns >> 7) & ~FileABB) | ((bPawns >> 9) & ~FileHBB);
// Evaluate pawns for both colors
// Evaluate pawns for both colors and weight the result
pi->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, pi)
- evaluate_pawns<BLACK>(pos, bPawns, wPawns, pi);
pi->value = apply_weight(pi->value, PawnStructureWeight);
return pi;
}
@@ -145,20 +116,23 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
template<Color Us>
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnInfo* pi) const {
Bitboard theirPawns, PawnInfo* pi) {
const BitCountType Max15 = CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b;
Square s;
File f;
Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = SCORE_ZERO;
const BitCountType Max15 = CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Square* ptr = pos.piece_list_begin(Us, PAWN);
// Loop through all pawns of the current color and score each pawn
while ((s = *ptr++) != SQ_NONE)
{
assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN));
assert(pos.piece_on(s) == make_piece(Us, PAWN));
f = square_file(s);
r = square_rank(s);
@@ -166,11 +140,11 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
// This file cannot be half open
pi->halfOpenFiles[Us] &= ~(1 << f);
// Our rank plus previous one. Used for chain detection.
// Our rank plus previous one. Used for chain detection
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1));
// Passed, isolated, doubled or member of a pawn
// chain (but not the backward one) ?
// Flag the pawn as passed, isolated, doubled or member of a pawn
// chain (but not the backward one).
passed = !(theirPawns & passed_pawn_mask(Us, s));
doubled = ourPawns & squares_in_front_of(Us, s);
opposed = theirPawns & squares_in_front_of(Us, s);
@@ -178,15 +152,13 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
chain = ourPawns & neighboring_files_bb(f) & b;
// Test for backward pawn
//
backward = false;
// If the pawn is passed, isolated, or member of a pawn chain
// it cannot be backward. If can capture an enemy pawn or if
// there are friendly pawns behind on neighboring files it cannot
// be backward either.
// If the pawn is passed, isolated, or member of a pawn chain it cannot
// be backward. If there are friendly pawns behind on neighboring files
// or if can capture an enemy pawn it cannot be backward either.
if ( !(passed | isolated | chain)
&& !(ourPawns & attack_span_mask(opposite_color(Us), s))
&& !(ourPawns & attack_span_mask(Them, s))
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
{
// We now know that there are no friendly pawns beside or behind this
@@ -200,21 +172,24 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
while (!(b & (ourPawns | theirPawns)))
Us == WHITE ? b <<= 8 : b >>= 8;
// The friendly pawn needs to be at least two ranks closer than the enemy
// pawn in order to help the potentially backward pawn advance.
// The friendly pawn needs to be at least two ranks closer than the
// enemy pawn in order to help the potentially backward pawn advance.
backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns;
}
assert(passed | opposed | (attack_span_mask(Us, s) & theirPawns));
assert(opposed | passed | (attack_span_mask(Us, s) & theirPawns));
// Test for candidate passed pawn
candidate = !(opposed | passed)
&& (b = attack_span_mask(opposite_color(Us), s + pawn_push(Us)) & ourPawns) != EmptyBoardBB
// A not passed pawn is a candidate to become passed if it is free to
// advance and if the number of friendly pawns beside or behind this
// pawn on neighboring files is higher or equal than the number of
// enemy pawns in the forward direction on the neighboring files.
candidate = !(opposed | passed | backward | isolated)
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != EmptyBoardBB
&& count_1s<Max15>(b) >= count_1s<Max15>(attack_span_mask(Us, s) & theirPawns);
// Mark the pawn as passed. Pawn will be properly scored in evaluation
// because we need full attack info to evaluate passed pawns. Only the
// frontmost passed pawn on each file is considered a true passed pawn.
// Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate passed pawns. Only the frontmost passed
// pawn on each file is considered a true passed pawn.
if (passed && !doubled)
set_bit(&(pi->passedPawns[Us]), s);
@@ -236,3 +211,33 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
}
return value;
}
/// PawnInfo::updateShelter() calculates and caches king shelter. It is called
/// only when king square changes, about 20% of total king_shelter() calls.
template<Color Us>
Score PawnInfo::updateShelter(const Position& pos, Square ksq) {
const int Shift = (Us == WHITE ? 8 : -8);
Bitboard pawns;
int r, shelter = 0;
if (relative_rank(Us, ksq) <= RANK_4)
{
pawns = pos.pieces(PAWN, Us) & this_and_neighboring_files_bb(ksq);
r = ksq & (7 << 3);
for (int i = 0; i < 3; i++)
{
r += Shift;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (64 >> i);
}
}
kingSquares[Us] = ksq;
kingShelters[Us] = make_score(shelter, 0);
return kingShelters[Us];
}
// Explicit template instantiation
template Score PawnInfo::updateShelter<WHITE>(const Position& pos, Square ksq);
template Score PawnInfo::updateShelter<BLACK>(const Position& pos, Square ksq);
+8 -64
View File
@@ -17,21 +17,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(PAWNS_H_INCLUDED)
#define PAWNS_H_INCLUDED
////
//// Includes
////
#include "bitboard.h"
#include "position.h"
#include "value.h"
////
//// Types
////
#include "tt.h"
#include "types.h"
const int PawnTableSize = 16384;
@@ -41,6 +32,7 @@ const int PawnTableSize = 16384;
/// to add further information in the future. A lookup to the pawn hash table
/// (performed by calling the get_pawn_info method in a PawnInfoTable object)
/// returns a pointer to a PawnInfo object.
class PawnInfo {
friend class PawnInfoTable;
@@ -69,43 +61,20 @@ private:
Score kingShelters[2];
};
/// The PawnInfoTable class represents a pawn hash table. It is basically
/// just an array of PawnInfo objects and a few methods for accessing these
/// objects. The most important method is get_pawn_info, which looks up a
/// position in the table and returns a pointer to a PawnInfo object.
class PawnInfoTable {
enum SideType { KingSide, QueenSide };
PawnInfoTable(const PawnInfoTable&);
PawnInfoTable& operator=(const PawnInfoTable&);
/// The PawnInfoTable class represents a pawn hash table. The most important
/// method is get_pawn_info, which returns a pointer to a PawnInfo object.
class PawnInfoTable : public SimpleHash<PawnInfo, PawnTableSize> {
public:
PawnInfoTable();
~PawnInfoTable();
PawnInfo* get_pawn_info(const Position& pos) const;
void prefetch(Key key) const;
private:
template<Color Us>
Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi) const;
PawnInfo* entries;
static Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi);
};
////
//// Inline functions
////
inline void PawnInfoTable::prefetch(Key key) const {
unsigned index = unsigned(key & (PawnTableSize - 1));
PawnInfo* pi = entries + index;
::prefetch((char*) pi);
}
inline Score PawnInfo::pawns_value() const {
return value;
}
@@ -119,7 +88,7 @@ inline Bitboard PawnInfo::passed_pawns(Color c) const {
}
inline int PawnInfo::file_is_half_open(Color c, File f) const {
return (halfOpenFiles[c] & (1 << int(f)));
return halfOpenFiles[c] & (1 << int(f));
}
inline int PawnInfo::has_open_file_to_left(Color c, File f) const {
@@ -130,31 +99,6 @@ inline int PawnInfo::has_open_file_to_right(Color c, File f) const {
return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
}
/// PawnInfo::updateShelter() calculates and caches king shelter. It is called
/// only when king square changes, about 20% of total king_shelter() calls.
template<Color Us>
Score PawnInfo::updateShelter(const Position& pos, Square ksq) {
const int Shift = (Us == WHITE ? 8 : -8);
Bitboard pawns;
int r, shelter = 0;
if (relative_rank(Us, ksq) <= RANK_4)
{
pawns = pos.pieces(PAWN, Us) & this_and_neighboring_files_bb(ksq);
r = square_rank(ksq) * 8;
for (int i = 1; i < 4; i++)
{
r += Shift;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (128 >> i);
}
}
kingSquares[Us] = ksq;
kingShelters[Us] = make_score(shelter, 0);
return kingShelters[Us];
}
template<Color Us>
inline Score PawnInfo::king_shelter(const Position& pos, Square ksq) {
return kingSquares[Us] == ksq ? kingShelters[Us] : updateShelter<Us>(pos, ksq);
-111
View File
@@ -1,111 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(PIECE_H_INCLUDED)
#define PIECE_H_INCLUDED
////
//// Includes
////
#include <string>
#include "color.h"
#include "square.h"
#include "value.h"
////
//// Types
////
enum PieceType {
PIECE_TYPE_NONE = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
};
enum Piece {
PIECE_NONE_DARK_SQ = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6,
BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14, PIECE_NONE = 16
};
ENABLE_OPERATORS_ON(PieceType);
ENABLE_OPERATORS_ON(Piece);
////
//// Constants
////
/// Important: If the material values are changed, one must also
/// adjust the piece square tables, and the method game_phase() in the
/// Position class!
///
/// Values modified by Joona Kiiski
const Value PawnValueMidgame = Value(0x0C6);
const Value PawnValueEndgame = Value(0x102);
const Value KnightValueMidgame = Value(0x331);
const Value KnightValueEndgame = Value(0x34E);
const Value BishopValueMidgame = Value(0x344);
const Value BishopValueEndgame = Value(0x359);
const Value RookValueMidgame = Value(0x4F6);
const Value RookValueEndgame = Value(0x4FE);
const Value QueenValueMidgame = Value(0x9D9);
const Value QueenValueEndgame = Value(0x9FE);
////
//// Inline functions
////
inline PieceType type_of_piece(Piece p) {
return PieceType(int(p) & 7);
}
inline Color color_of_piece(Piece p) {
return Color(int(p) >> 3);
}
inline Piece piece_of_color_and_type(Color c, PieceType pt) {
return Piece((int(c) << 3) | int(pt));
}
inline SquareDelta pawn_push(Color c) {
return (c == WHITE ? DELTA_N : DELTA_S);
}
inline bool piece_type_is_ok(PieceType pt) {
return pt >= PAWN && pt <= KING;
}
inline bool piece_is_ok(Piece p) {
return piece_type_is_ok(type_of_piece(p)) && color_is_ok(color_of_piece(p));
}
inline char piece_type_to_char(PieceType pt) {
return std::string(" PNBRQK")[pt];
}
inline PieceType piece_type_from_char(char c) {
return PieceType(std::string(" PNBRQK").find(c));
}
#endif // !defined(PIECE_H_INCLUDED)
+234 -180
View File
@@ -17,12 +17,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <algorithm>
#include <cassert>
#include <cstring>
#include <fstream>
@@ -32,11 +26,10 @@
#include "bitcount.h"
#include "movegen.h"
#include "movepick.h"
#include "position.h"
#include "psqtab.h"
#include "rkiss.h"
#include "san.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h"
@@ -44,11 +37,6 @@ using std::string;
using std::cout;
using std::endl;
////
//// Position's static data definitions
////
Key Position::zobrist[2][8][64];
Key Position::zobEp[64];
Key Position::zobCastle[16];
@@ -89,8 +77,6 @@ namespace {
// Bonus for having the side to move (modified by Joona Kiiski)
const Score TempoValue = make_score(48, 22);
bool isZero(char c) { return c == '0'; }
struct PieceLetters : public std::map<char, Piece> {
PieceLetters() {
@@ -152,9 +138,9 @@ Position::Position(const Position& pos, int th) {
nodes = 0;
}
Position::Position(const string& fen, int th) {
Position::Position(const string& fen, bool isChess960, int th) {
from_fen(fen);
from_fen(fen, isChess960);
threadID = th;
}
@@ -175,14 +161,14 @@ void Position::detach() {
/// string. This function is not very robust - make sure that input FENs are
/// correct (this is assumed to be the responsibility of the GUI).
void Position::from_fen(const string& fen) {
void Position::from_fen(const string& fen, bool isChess960) {
/*
A FEN string defines a particular position using only the ASCII character set.
A FEN string contains six fields. The separator between fields is a space. The fields are:
1) Piece placement (from white's perspective). Each rank is described, starting with rank 8 and ending
with rank 1; within each rank, the contents of each square are described from file a through file h.
with rank 1; within each rank, the contents of each square are described from file A through file H.
Following the Standard Algebraic Notation (SAN), each piece is identified by a single letter taken
from the standard English names. White pieces are designated using upper-case letters ("PNBRQK")
while Black take lowercase ("pnbrqk"). Blank squares are noted using digits 1 through 8 (the number
@@ -205,32 +191,26 @@ void Position::from_fen(const string& fen) {
*/
char token;
int hmc, fmn;
std::istringstream ss(fen);
Rank rank = RANK_8;
File file = FILE_A;
Square sq = SQ_A8;
clear();
// 1. Piece placement field
while (ss.get(token) && token != ' ')
{
if (isdigit(token))
if (pieceLetters.find(token) != pieceLetters.end())
{
file += File(token - '0'); // Skip the given number of files
continue;
put_piece(pieceLetters[token], sq);
sq++;
}
else if (isdigit(token))
sq += Square(token - '0'); // Skip the given number of files
else if (token == '/')
{
file = FILE_A;
rank--;
continue;
}
if (pieceLetters.find(token) == pieceLetters.end())
sq -= SQ_A3; // Jump back of 2 rows
else
goto incorrect_fen;
put_piece(pieceLetters[token], make_square(file, rank));
file++;
}
// 2. Active color
@@ -244,27 +224,29 @@ void Position::from_fen(const string& fen) {
// 3. Castling availability
while (ss.get(token) && token != ' ')
{
if (token == '-')
continue;
if (!set_castling_rights(token))
goto incorrect_fen;
}
// 4. En passant square -- ignore if no capture is possible
// 4. En passant square
char col, row;
if ( (ss.get(col) && (col >= 'a' && col <= 'h'))
&& (ss.get(row) && (row == '3' || row == '6')))
{
Square fenEpSquare = make_square(file_from_char(col), rank_from_char(row));
Color them = opposite_color(sideToMove);
st->epSquare = make_square(file_from_char(col), rank_from_char(row));
if (attacks_from<PAWN>(fenEpSquare, them) & pieces(PAWN, sideToMove))
st->epSquare = fenEpSquare;
// Ignore if no capture is possible
Color them = opposite_color(sideToMove);
if (!(attacks_from<PAWN>(st->epSquare, them) & pieces(PAWN, sideToMove)))
st->epSquare = SQ_NONE;
}
// 5-6. Halfmove clock and fullmove number are not parsed
// 5. Halfmove clock
if (ss >> hmc)
st->rule50 = hmc;
// 6. Fullmove number
if (ss >> fmn)
startPosPlyCounter = (fmn - 1) * 2 + int(sideToMove == BLACK);
// Various initialisations
castleRightsMask[make_square(initialKFile, RANK_1)] ^= WHITE_OO | WHITE_OOO;
@@ -274,10 +256,7 @@ void Position::from_fen(const string& fen) {
castleRightsMask[make_square(initialQRFile, RANK_1)] ^= WHITE_OOO;
castleRightsMask[make_square(initialQRFile, RANK_8)] ^= BLACK_OOO;
isChess960 = initialKFile != FILE_E
|| initialQRFile != FILE_A
|| initialKRFile != FILE_H;
chess960 = isChess960;
find_checkers();
st->key = compute_key();
@@ -344,7 +323,8 @@ bool Position::set_castling_rights(char token) {
initialKRFile = rookFile;
}
}
else return false;
else
return token == '-';
return true;
}
@@ -359,7 +339,7 @@ const string Position::to_fen() const {
Square sq;
char emptyCnt = '0';
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
for (Rank rank = RANK_8; rank >= RANK_1; rank--, fen += '/')
{
for (File file = FILE_A; file <= FILE_H; file++)
{
@@ -367,34 +347,38 @@ const string Position::to_fen() const {
if (square_is_occupied(sq))
{
fen += emptyCnt;
if (emptyCnt != '0')
{
fen += emptyCnt;
emptyCnt = '0';
}
fen += pieceLetters.from_piece(piece_on(sq));
emptyCnt = '0';
} else
emptyCnt++;
}
fen += emptyCnt;
fen += '/';
emptyCnt = '0';
if (emptyCnt != '0')
{
fen += emptyCnt;
emptyCnt = '0';
}
}
fen.erase(std::remove_if(fen.begin(), fen.end(), isZero), fen.end());
fen.erase(--fen.end());
fen += (sideToMove == WHITE ? " w " : " b ");
if (st->castleRights != CASTLES_NONE)
{
if (can_castle_kingside(WHITE))
fen += isChess960 ? char(toupper(file_to_char(initialKRFile))) : 'K';
fen += chess960 ? char(toupper(file_to_char(initialKRFile))) : 'K';
if (can_castle_queenside(WHITE))
fen += isChess960 ? char(toupper(file_to_char(initialQRFile))) : 'Q';
fen += chess960 ? char(toupper(file_to_char(initialQRFile))) : 'Q';
if (can_castle_kingside(BLACK))
fen += isChess960 ? file_to_char(initialKRFile) : 'k';
fen += chess960 ? file_to_char(initialKRFile) : 'k';
if (can_castle_queenside(BLACK))
fen += isChess960 ? file_to_char(initialQRFile) : 'q';
fen += chess960 ? file_to_char(initialQRFile) : 'q';
} else
fen += '-';
@@ -409,14 +393,6 @@ const string Position::to_fen() const {
void Position::print(Move move) const {
const char* dottedLine = "\n+---+---+---+---+---+---+---+---+\n";
static bool requestPending = false;
// Check for reentrancy, as example when called from inside
// MovePicker that is used also here in move_to_san()
if (requestPending)
return;
requestPending = true;
if (move)
{
@@ -431,17 +407,16 @@ void Position::print(Move move) const {
for (File file = FILE_A; file <= FILE_H; file++)
{
Square sq = make_square(file, rank);
char c = (color_of_piece_on(sq) == BLACK ? '=' : ' ');
Piece piece = piece_on(sq);
if (piece == PIECE_NONE && square_color(sq) == DARK)
piece = PIECE_NONE_DARK_SQ;
char c = (color_of_piece_on(sq) == BLACK ? '=' : ' ');
cout << c << pieceLetters.from_piece(piece) << c << '|';
}
}
cout << dottedLine << "Fen is: " << to_fen() << "\nKey is: " << st->key << endl;
requestPending = false;
}
@@ -527,7 +502,7 @@ Bitboard Position::attacks_from(Piece p, Square s) const {
case WB: case BB: return attacks_from<BISHOP>(s);
case WR: case BR: return attacks_from<ROOK>(s);
case WQ: case BQ: return attacks_from<QUEEN>(s);
default: return StepAttackBB[p][s];
default: return StepAttacksBB[p][s];
}
}
@@ -540,7 +515,7 @@ Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) {
case WB: case BB: return bishop_attacks_bb(s, occ);
case WR: case BR: return rook_attacks_bb(s, occ);
case WQ: case BQ: return bishop_attacks_bb(s, occ) | rook_attacks_bb(s, occ);
default: return StepAttackBB[p][s];
default: return StepAttacksBB[p][s];
}
}
@@ -613,8 +588,8 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
Bitboard b = occupied_squares();
assert(to == ep_square());
assert(piece_on(from) == piece_of_color_and_type(us, PAWN));
assert(piece_on(capsq) == piece_of_color_and_type(them, PAWN));
assert(piece_on(from) == make_piece(us, PAWN));
assert(piece_on(capsq) == make_piece(them, PAWN));
assert(piece_on(to) == PIECE_NONE);
clear_bit(&b, from);
@@ -629,7 +604,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
Square from = move_from(m);
assert(color_of_piece_on(from) == us);
assert(piece_on(king_square(us)) == piece_of_color_and_type(us, KING));
assert(piece_on(king_square(us)) == make_piece(us, KING));
// If the moving piece is a king, check whether the destination
// square is attacked by the opponent.
@@ -648,7 +623,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
bool Position::pl_move_is_evasion(Move m, Bitboard pinned) const
{
assert(is_check());
assert(in_check());
Color us = side_to_move();
Square from = move_from(m);
@@ -669,21 +644,130 @@ bool Position::pl_move_is_evasion(Move m, Bitboard pinned) const
return bit_is_set(target, to) && pl_move_is_legal(m, pinned);
}
/// Position::move_is_legal() takes a position and a (not necessarily pseudo-legal)
/// move and tests whether the move is legal. This version is not very fast and
/// should be used only in non time-critical paths.
/// Position::move_is_check() tests whether a pseudo-legal move is a check
bool Position::move_is_legal(const Move m) const {
bool Position::move_is_check(Move m) const {
MoveStack mlist[MAX_MOVES];
MoveStack *cur, *last = generate<MV_PSEUDO_LEGAL>(*this, mlist);
return move_is_check(m, CheckInfo(*this));
for (cur = mlist; cur != last; cur++)
if (cur->move == m)
return pl_move_is_legal(m, pinned_pieces(sideToMove));
return false;
}
bool Position::move_is_check(Move m, const CheckInfo& ci) const {
/// Fast version of Position::move_is_legal() that takes a position a move and
/// a bitboard of pinned pieces as input, and tests whether the move is legal.
bool Position::move_is_legal(const Move m, Bitboard pinned) const {
assert(is_ok());
assert(pinned == pinned_pieces(sideToMove));
Color us = sideToMove;
Color them = opposite_color(sideToMove);
Square from = move_from(m);
Square to = move_to(m);
Piece pc = piece_on(from);
// Use a slower but simpler function for uncommon cases
if (move_is_special(m))
return move_is_legal(m);
// If the from square is not occupied by a piece belonging to the side to
// move, the move is obviously not legal.
if (color_of_piece(pc) != us)
return false;
// The destination square cannot be occupied by a friendly piece
if (color_of_piece_on(to) == us)
return false;
// Handle the special case of a pawn move
if (type_of_piece(pc) == PAWN)
{
// Move direction must be compatible with pawn color
int direction = to - from;
if ((us == WHITE) != (direction > 0))
return false;
// We have already handled promotion moves, so destination
// cannot be on the 8/1th rank.
if (square_rank(to) == RANK_8 || square_rank(to) == RANK_1)
return false;
// Proceed according to the square delta between the origin and
// destination squares.
switch (direction)
{
case DELTA_NW:
case DELTA_NE:
case DELTA_SW:
case DELTA_SE:
// Capture. The destination square must be occupied by an enemy
// piece (en passant captures was handled earlier).
if (color_of_piece_on(to) != them)
return false;
break;
case DELTA_N:
case DELTA_S:
// Pawn push. The destination square must be empty.
if (!square_is_empty(to))
return false;
break;
case DELTA_NN:
// Double white pawn push. The destination square must be on the fourth
// rank, and both the destination square and the square between the
// source and destination squares must be empty.
if ( square_rank(to) != RANK_4
|| !square_is_empty(to)
|| !square_is_empty(from + DELTA_N))
return false;
break;
case DELTA_SS:
// Double black pawn push. The destination square must be on the fifth
// rank, and both the destination square and the square between the
// source and destination squares must be empty.
if ( square_rank(to) != RANK_5
|| !square_is_empty(to)
|| !square_is_empty(from + DELTA_S))
return false;
break;
default:
return false;
}
}
else if (!bit_is_set(attacks_from(pc, from), to))
return false;
// The move is pseudo-legal, check if it is also legal
return in_check() ? pl_move_is_evasion(m, pinned) : pl_move_is_legal(m, pinned);
}
/// Position::move_gives_check() tests whether a pseudo-legal move is a check
bool Position::move_gives_check(Move m) const {
return move_gives_check(m, CheckInfo(*this));
}
bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
assert(is_ok());
assert(move_is_ok(m));
assert(ci.dcCandidates == discovered_check_candidates(side_to_move()));
assert(color_of_piece_on(move_from(m)) == side_to_move());
assert(piece_on(ci.ksq) == piece_of_color_and_type(opposite_color(side_to_move()), KING));
assert(piece_on(ci.ksq) == make_piece(opposite_color(side_to_move()), KING));
Square from = move_from(m);
Square to = move_to(m);
@@ -769,20 +853,44 @@ bool Position::move_is_check(Move m, const CheckInfo& ci) const {
}
/// Position::do_setup_move() makes a permanent move on the board. It should
/// be used when setting up a position on board. You can't undo the move.
void Position::do_setup_move(Move m) {
StateInfo newSt;
do_move(m, newSt);
// Reset "game ply" in case we made a non-reversible move.
// "game ply" is used for repetition detection.
if (st->rule50 == 0)
st->gamePly = 0;
// Update the number of plies played from the starting position
startPosPlyCounter++;
// Our StateInfo newSt is about going out of scope so copy
// its content before it disappears.
detach();
}
/// Position::do_move() makes a move, and saves all information necessary
/// to a StateInfo object. The move is assumed to be legal.
/// Pseudo-legal moves should be filtered out before this function is called.
/// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
/// moves should be filtered out before this function is called.
void Position::do_move(Move m, StateInfo& newSt) {
CheckInfo ci(*this);
do_move(m, newSt, ci, move_is_check(m, ci));
do_move(m, newSt, ci, move_gives_check(m, ci));
}
void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) {
assert(is_ok());
assert(move_is_ok(m));
assert(&newSt != st);
nodes++;
Key key = st->key;
@@ -798,8 +906,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
Value npMaterial[2];
};
if (&newSt != st)
memcpy(&newSt, st, sizeof(ReducedStateInfo));
memcpy(&newSt, st, sizeof(ReducedStateInfo));
newSt.previous = st;
st = &newSt;
@@ -836,7 +943,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
assert(color_of_piece_on(from) == us);
assert(color_of_piece_on(to) == them || square_is_empty(to));
assert(!(ep || pm) || piece == piece_of_color_and_type(us, PAWN));
assert(!(ep || pm) || piece == make_piece(us, PAWN));
assert(!pm || relative_rank(us, to) == RANK_8);
if (capture)
@@ -888,7 +995,6 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Update pawn hash key and prefetch in L1/L2 cache
st->pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
prefetchPawn(st->pawnKey, threadID);
// Set en passant square, only if moved pawn can be captured
if ((to ^ from) == 16)
@@ -909,7 +1015,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Insert promoted piece instead of pawn
clear_bit(&(byTypeBB[PAWN]), to);
set_bit(&(byTypeBB[promotion]), to);
board[to] = piece_of_color_and_type(us, promotion);
board[to] = make_piece(us, promotion);
// Update piece counts
pieceCount[us][promotion]++;
@@ -941,6 +1047,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
}
}
// Prefetch pawn and material hash tables
Threads[threadID].pawnTable.prefetch(st->pawnKey);
Threads[threadID].materialTable.prefetch(st->materialKey);
// Update incremental scores
st->value += pst_delta(piece, from, to);
@@ -1003,7 +1113,7 @@ void Position::do_capture_move(Key& key, PieceType capture, Color them, Square t
assert(to == st->epSquare);
assert(relative_rank(opposite_color(them), to) == RANK_6);
assert(piece_on(to) == PIECE_NONE);
assert(piece_on(capsq) == piece_of_color_and_type(them, PAWN));
assert(piece_on(capsq) == make_piece(them, PAWN));
board[capsq] = PIECE_NONE;
}
@@ -1067,8 +1177,8 @@ void Position::do_castle_move(Move m) {
Square rfrom = move_to(m); // HACK: See comment at beginning of function
Square kto, rto;
assert(piece_on(kfrom) == piece_of_color_and_type(us, KING));
assert(piece_on(rfrom) == piece_of_color_and_type(us, ROOK));
assert(piece_on(kfrom) == make_piece(us, KING));
assert(piece_on(rfrom) == make_piece(us, ROOK));
// Find destination squares for king and rook
if (rfrom > kfrom) // O-O
@@ -1097,8 +1207,8 @@ void Position::do_castle_move(Move m) {
set_bit(&(byTypeBB[0]), rto); // HACK: byTypeBB[0] == occupied squares
// Update board array
Piece king = piece_of_color_and_type(us, KING);
Piece rook = piece_of_color_and_type(us, ROOK);
Piece king = make_piece(us, KING);
Piece rook = make_piece(us, ROOK);
board[kfrom] = board[rfrom] = PIECE_NONE;
board[kto] = king;
board[rto] = rook;
@@ -1174,7 +1284,7 @@ void Position::undo_move(Move m) {
assert(!pm || relative_rank(us, to) == RANK_8);
assert(!ep || to == st->previous->epSquare);
assert(!ep || relative_rank(us, to) == RANK_6);
assert(!ep || piece_on(to) == piece_of_color_and_type(us, PAWN));
assert(!ep || piece_on(to) == make_piece(us, PAWN));
if (pm) // promotion ?
{
@@ -1182,7 +1292,7 @@ void Position::undo_move(Move m) {
pt = PAWN;
assert(promotion >= KNIGHT && promotion <= QUEEN);
assert(piece_on(to) == piece_of_color_and_type(us, promotion));
assert(piece_on(to) == make_piece(us, promotion));
// Replace promoted piece with a pawn
clear_bit(&(byTypeBB[promotion]), to);
@@ -1207,7 +1317,7 @@ void Position::undo_move(Move m) {
do_move_bb(&(byTypeBB[pt]), move_bb);
do_move_bb(&(byTypeBB[0]), move_bb); // HACK: byTypeBB[0] == occupied squares
board[from] = piece_of_color_and_type(us, pt);
board[from] = make_piece(us, pt);
board[to] = PIECE_NONE;
// Update piece list
@@ -1229,7 +1339,7 @@ void Position::undo_move(Move m) {
set_bit(&(byTypeBB[st->capturedType]), capsq);
set_bit(&(byTypeBB[0]), capsq);
board[capsq] = piece_of_color_and_type(them, st->capturedType);
board[capsq] = make_piece(them, st->capturedType);
// Update piece count
pieceCount[them][st->capturedType]++;
@@ -1276,8 +1386,8 @@ void Position::undo_castle_move(Move m) {
rto = relative_square(us, SQ_D1);
}
assert(piece_on(kto) == piece_of_color_and_type(us, KING));
assert(piece_on(rto) == piece_of_color_and_type(us, ROOK));
assert(piece_on(kto) == make_piece(us, KING));
assert(piece_on(rto) == make_piece(us, ROOK));
// Remove pieces from destination squares:
clear_bit(&(byColorBB[us]), kto);
@@ -1297,8 +1407,8 @@ void Position::undo_castle_move(Move m) {
// Update board
board[rto] = board[kto] = PIECE_NONE;
board[rfrom] = piece_of_color_and_type(us, ROOK);
board[kfrom] = piece_of_color_and_type(us, KING);
board[rfrom] = make_piece(us, ROOK);
board[kfrom] = make_piece(us, KING);
// Update piece lists
pieceList[us][KING][index[kto]] = kfrom;
@@ -1320,7 +1430,7 @@ void Position::undo_castle_move(Move m) {
void Position::do_null_move(StateInfo& backupSt) {
assert(is_ok());
assert(!is_check());
assert(!in_check());
// Back up the information necessary to undo the null move to the supplied
// StateInfo object.
@@ -1357,7 +1467,7 @@ void Position::do_null_move(StateInfo& backupSt) {
void Position::undo_null_move() {
assert(is_ok());
assert(!is_check());
assert(!in_check());
// Restore information from the our backup StateInfo object
StateInfo* backupSt = st->previous;
@@ -1536,22 +1646,6 @@ void Position::clear() {
}
/// Position::reset_game_ply() simply sets gamePly to 0. It is used from the
/// UCI interface code, whenever a non-reversible move is made in a
/// 'position fen <fen> moves m1 m2 ...' command. This makes it possible
/// for the program to handle games of arbitrary length, as long as the GUI
/// handles draws by the 50 move rule correctly.
void Position::reset_game_ply() {
st->gamePly = 0;
}
void Position::inc_startpos_ply_counter() {
startPosPlyCounter++;
}
/// Position::put_piece() puts a piece on the given square of the board,
/// updating the board array, pieces list, bitboards, and piece counts.
@@ -1686,7 +1780,7 @@ bool Position::is_draw() const {
return true;
// Draw by the 50 moves rule?
if (st->rule50 > 99 && (st->rule50 > 100 || !is_mate()))
if (st->rule50 > 99 && !is_mate())
return true;
// Draw by repetition?
@@ -1703,51 +1797,8 @@ bool Position::is_draw() const {
bool Position::is_mate() const {
MoveStack moves[MOVES_MAX];
return is_check() && generate_moves(*this, moves) == moves;
}
/// Position::has_mate_threat() tests whether the side to move is under
/// a threat of being mated in one from the current position.
bool Position::has_mate_threat() {
MoveStack mlist[MOVES_MAX], *last, *cur;
StateInfo st1, st2;
bool mateFound = false;
// If we are under check it's up to evasions to do the job
if (is_check())
return false;
// First pass the move to our opponent doing a null move
do_null_move(st1);
// Then generate pseudo-legal moves that could give check
last = generate_non_capture_checks(*this, mlist);
last = generate_captures(*this, last);
// Loop through the moves, and see if one of them gives mate
Bitboard pinned = pinned_pieces(sideToMove);
CheckInfo ci(*this);
for (cur = mlist; cur != last && !mateFound; cur++)
{
Move move = cur->move;
if ( !pl_move_is_legal(move, pinned)
|| !move_is_check(move, ci))
continue;
do_move(move, st2, ci, true);
if (is_mate())
mateFound = true;
undo_move(move);
}
undo_null_move();
return mateFound;
MoveStack moves[MAX_MOVES];
return in_check() && generate<MV_LEGAL>(*this, moves) == moves;
}
@@ -1791,13 +1842,15 @@ void Position::init_piece_square_tables() {
}
/// Position::flipped_copy() makes a copy of the input position, but with
/// the white and black sides reversed. This is only useful for debugging,
/// especially for finding evaluation symmetry bugs.
/// Position::flip() flips position with the white and black sides reversed. This
/// is only useful for debugging especially for finding evaluation symmetry bugs.
void Position::flipped_copy(const Position& pos) {
void Position::flip() {
assert(pos.is_ok());
assert(is_ok());
// Make a copy of current position before to start changing
const Position pos(*this, threadID);
clear();
threadID = pos.thread();
@@ -1994,25 +2047,26 @@ bool Position::is_ok(int* failedStep) const {
if (failedStep) (*failedStep)++;
if (debugPieceList)
{
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (int i = 0; i < pieceCount[c][pt]; i++)
{
if (piece_on(piece_list(c, pt, i)) != piece_of_color_and_type(c, pt))
if (piece_on(piece_list(c, pt, i)) != make_piece(c, pt))
return false;
if (index[piece_list(c, pt, i)] != i)
return false;
}
}
if (failedStep) (*failedStep)++;
if (debugCastleSquares) {
for (Color c = WHITE; c <= BLACK; c++) {
if (can_castle_kingside(c) && piece_on(initial_kr_square(c)) != piece_of_color_and_type(c, ROOK))
if (debugCastleSquares)
{
for (Color c = WHITE; c <= BLACK; c++)
{
if (can_castle_kingside(c) && piece_on(initial_kr_square(c)) != make_piece(c, ROOK))
return false;
if (can_castle_queenside(c) && piece_on(initial_qr_square(c)) != piece_of_color_and_type(c, ROOK))
if (can_castle_queenside(c) && piece_on(initial_qr_square(c)) != make_piece(c, ROOK))
return false;
}
if (castleRightsMask[initial_kr_square(WHITE)] != (ALL_CASTLES ^ WHITE_OO))
+23 -57
View File
@@ -17,35 +17,19 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(POSITION_H_INCLUDED)
#define POSITION_H_INCLUDED
////
//// Includes
////
#include "bitboard.h"
#include "color.h"
#include "move.h"
#include "piece.h"
#include "square.h"
#include "value.h"
////
//// Constants
////
#include "types.h"
/// Maximum number of plies per game (220 should be enough, because the
/// maximum search depth is 100, and during position setup we reset the
/// move counter for every non-reversible move).
const int MaxGameLength = 220;
////
//// Types
////
class Position;
/// struct checkInfo is initialized at c'tor time and keeps
/// info used to detect if a move gives check.
@@ -120,9 +104,6 @@ struct StateInfo {
class Position {
friend class MaterialInfo;
friend class EndgameFunctions;
Position(); // No default or copy c'tor allowed
Position(const Position& pos);
@@ -134,15 +115,15 @@ public:
// Constructors
Position(const Position& pos, int threadID);
Position(const std::string& fen, int threadID);
Position(const std::string& fen, bool isChess960, int threadID);
// Text input/output
void from_fen(const std::string& fen);
void from_fen(const std::string& fen, bool isChess960);
const std::string to_fen() const;
void print(Move m = MOVE_NONE) const;
// Copying
void flipped_copy(const Position& pos);
void flip();
// The piece on a given square
Piece piece_on(Square s) const;
@@ -187,7 +168,7 @@ public:
// Checking pieces and under check information
Bitboard checkers() const;
bool is_check() const;
bool in_check() const;
// Piece lists
Square piece_list(Color c, PieceType pt, int index) const;
@@ -203,8 +184,10 @@ public:
// Properties of moves
bool pl_move_is_legal(Move m, Bitboard pinned) const;
bool pl_move_is_evasion(Move m, Bitboard pinned) const;
bool move_is_check(Move m) const;
bool move_is_check(Move m, const CheckInfo& ci) const;
bool move_is_legal(const Move m) const;
bool move_is_legal(const Move m, Bitboard pinned) const;
bool move_gives_check(Move m) const;
bool move_gives_check(Move m, const CheckInfo& ci) const;
bool move_is_capture(Move m) const;
bool move_is_capture_or_promotion(Move m) const;
bool move_is_passed_pawn_push(Move m) const;
@@ -220,7 +203,7 @@ public:
bool square_is_weak(Square s, Color c) const;
// Doing and undoing moves
void detach();
void do_setup_move(Move m);
void do_move(Move m, StateInfo& st);
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
void undo_move(Move m);
@@ -247,12 +230,7 @@ public:
bool is_mate() const;
bool is_draw() const;
// Check if side to move could be mated in one
bool has_mate_threat();
// Number of plies since the last non-reversible move
int rule_50_counter() const;
// Number of plies from starting position
int startpos_ply_counter() const;
// Other properties of the position
@@ -263,9 +241,6 @@ public:
// Current thread ID searching on the position
int thread() const;
// Reset the gamePly variable to 0
void reset_game_ply();
void inc_startpos_ply_counter();
int64_t nodes_searched() const;
void set_nodes_searched(int64_t n);
@@ -280,6 +255,7 @@ private:
// Initialization helper functions (used while setting up a position)
void clear();
void detach();
void put_piece(Piece p, Square s);
void do_allow_oo(Color c);
void do_allow_ooo(Color c);
@@ -323,7 +299,7 @@ private:
int castleRightsMask[64];
StateInfo startState;
File initialKFile, initialKRFile, initialQRFile;
bool isChess960;
bool chess960;
int startPosPlyCounter;
int threadID;
int64_t nodes;
@@ -341,11 +317,6 @@ private:
static const Value PieceValueEndgame[17];
};
////
//// Inline functions
////
inline int64_t Position::nodes_searched() const {
return nodes;
}
@@ -456,12 +427,12 @@ inline Square Position::initial_qr_square(Color c) const {
template<>
inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
return StepAttackBB[piece_of_color_and_type(c, PAWN)][s];
return StepAttacksBB[make_piece(c, PAWN)][s];
}
template<PieceType Piece> // Knight and King and white pawns
inline Bitboard Position::attacks_from(Square s) const {
return StepAttackBB[Piece][s];
return StepAttacksBB[Piece][s];
}
template<>
@@ -483,7 +454,7 @@ inline Bitboard Position::checkers() const {
return st->checkersBB;
}
inline bool Position::is_check() const {
inline bool Position::in_check() const {
return st->checkersBB != EmptyBoardBB;
}
@@ -512,7 +483,7 @@ inline Key Position::get_material_key() const {
}
inline Score Position::pst(Color c, PieceType pt, Square s) {
return PieceSquareTable[piece_of_color_and_type(c, pt)][s];
return PieceSquareTable[make_piece(c, pt)][s];
}
inline Score Position::pst_delta(Piece piece, Square from, Square to) {
@@ -530,31 +501,26 @@ inline Value Position::non_pawn_material(Color c) const {
inline bool Position::move_is_passed_pawn_push(Move m) const {
Color c = side_to_move();
return piece_on(move_from(m)) == piece_of_color_and_type(c, PAWN)
return piece_on(move_from(m)) == make_piece(c, PAWN)
&& pawn_is_passed(c, move_to(m));
}
inline int Position::rule_50_counter() const {
return st->rule50;
}
inline int Position::startpos_ply_counter() const {
return startPosPlyCounter;
}
inline bool Position::opposite_colored_bishops() const {
return piece_count(WHITE, BISHOP) == 1
&& piece_count(BLACK, BISHOP) == 1
&& !same_color_squares(piece_list(WHITE, BISHOP, 0), piece_list(BLACK, BISHOP, 0));
return piece_count(WHITE, BISHOP) == 1 && piece_count(BLACK, BISHOP) == 1
&& opposite_color_squares(piece_list(WHITE, BISHOP, 0), piece_list(BLACK, BISHOP, 0));
}
inline bool Position::has_pawn_on_7th(Color c) const {
return pieces(PAWN, c) & relative_rank_bb(c, RANK_7);
return pieces(PAWN, c) & rank_bb(relative_rank(c, RANK_7));
}
inline bool Position::is_chess960() const {
return isChess960;
return chess960;
}
inline bool Position::move_is_capture(Move m) const {
+1 -6
View File
@@ -17,15 +17,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(PSQTAB_H_INCLUDED)
#define PSQTAB_H_INCLUDED
////
//// Includes
////
#include "value.h"
#include "types.h"
namespace {
+18 -25
View File
@@ -21,7 +21,9 @@
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
** A small "keep it simple and stupid" RNG with some fancy merits:
** George Marsaglia invented the RNG-Kiss-family in the early 90's.
** This is a specific version that Heinz van Saanen derived and
** tested from some public domain code by Bob Jenkins:
**
** Quite platform independent
** Passes ALL dieharder tests! Here *nix sys-rand() e.g. fails miserably:-)
@@ -31,49 +33,40 @@
** 64 bit seed
** Return doubles with a full 53 bit mantissa
** Thread safe
**
** (c) Heinz van Saanen
*/
#if !defined(RKISS_H_INCLUDED)
#define RKISS_H_INCLUDED
////
//// Includes
////
#include "types.h"
////
//// Types
////
class RKISS {
// Keep variables always together
struct S { uint64_t a, b, c, d; } s;
// Return 64 bit unsigned integer in between [0,2^64-1]
uint64_t rotate(uint64_t x, uint64_t k) const {
return (x << k) | (x >> (64 - k));
}
// Return 64 bit unsigned integer in between [0, 2^64 - 1]
uint64_t rand64() {
const uint64_t
e = s.a - ((s.b << 7) | (s.b >> 57));
s.a = s.b ^ ((s.c << 13) | (s.c >> 51));
s.b = s.c + ((s.d << 37) | (s.d >> 27));
s.c = s.d + e;
return s.d = e + s.a;
const uint64_t
e = s.a - rotate(s.b, 7);
s.a = s.b ^ rotate(s.c, 13);
s.b = s.c + rotate(s.d, 37);
s.c = s.d + e;
return s.d = e + s.a;
}
// Init seed and scramble a few rounds
void raninit() {
s.a = 0xf1ea5eed;
s.b = s.c = s.d = 0xd4e12c77;
for (int i = 0; i < 73; i++)
rand64();
s.a = 0xf1ea5eed;
s.b = s.c = s.d = 0xd4e12c77;
for (int i = 0; i < 73; i++)
rand64();
}
public:
-439
View File
@@ -1,439 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include <cstring>
#include <iomanip>
#include <string>
#include <sstream>
#include "movegen.h"
#include "san.h"
using std::string;
////
//// Local definitions
////
namespace {
enum Ambiguity {
AMBIGUITY_NONE, AMBIGUITY_FILE, AMBIGUITY_RANK, AMBIGUITY_BOTH
};
Ambiguity move_ambiguity(const Position& pos, Move m);
const string time_string(int milliseconds);
const string score_string(Value v);
}
////
//// Functions
////
/// move_to_san() takes a position and a move as input, where it is assumed
/// that the move is a legal move from the position. The return value is
/// a string containing the move in short algebraic notation.
const string move_to_san(Position& pos, Move m) {
assert(pos.is_ok());
assert(move_is_ok(m));
string san;
Square from = move_from(m);
Square to = move_to(m);
PieceType pt = type_of_piece(pos.piece_on(from));
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "(null)";
if (move_is_long_castle(m))
san = "O-O-O";
else if (move_is_short_castle(m))
san = "O-O";
else
{
if (pt != PAWN)
{
san += piece_type_to_char(pt);
switch (move_ambiguity(pos, m)) {
case AMBIGUITY_NONE:
break;
case AMBIGUITY_FILE:
san += file_to_char(square_file(from));
break;
case AMBIGUITY_RANK:
san += rank_to_char(square_rank(from));
break;
case AMBIGUITY_BOTH:
san += square_to_string(from);
break;
default:
assert(false);
}
}
if (pos.move_is_capture(m))
{
if (pt == PAWN)
san += file_to_char(square_file(from));
san += 'x';
}
san += square_to_string(to);
if (move_is_promotion(m))
{
san += '=';
san += piece_type_to_char(move_promotion_piece(m));
}
}
// The move gives check ? We don't use pos.move_is_check() here
// because we need to test for mate after the move is done.
StateInfo st;
pos.do_move(m, st);
if (pos.is_check())
san += pos.is_mate() ? "#" : "+";
pos.undo_move(m);
return san;
}
/// move_from_san() takes a position and a string as input, and tries to
/// interpret the string as a move in short algebraic notation. On success,
/// the move is returned. On failure (i.e. if the string is unparsable, or
/// if the move is illegal or ambiguous), MOVE_NONE is returned.
Move move_from_san(const Position& pos, const string& movestr) {
assert(pos.is_ok());
enum { START, TO_FILE, TO_RANK, PROMOTION_OR_CHECK, PROMOTION, CHECK, END };
static const string pieceLetters = "KQRBN";
MoveStack mlist[MOVES_MAX], *last;
PieceType pt = PIECE_TYPE_NONE, promotion = PIECE_TYPE_NONE;
File fromFile = FILE_NONE, toFile = FILE_NONE;
Rank fromRank = RANK_NONE, toRank = RANK_NONE;
Move move = MOVE_NONE;
Square from, to;
int matches, state = START;
// Generate all legal moves for the given position
last = generate_moves(pos, mlist);
// Castling moves
if (movestr == "O-O-O" || movestr == "O-O-O+")
{
for (MoveStack* cur = mlist; cur != last; cur++)
if (move_is_long_castle(cur->move))
return cur->move;
return MOVE_NONE;
}
else if (movestr == "O-O" || movestr == "O-O+")
{
for (MoveStack* cur = mlist; cur != last; cur++)
if (move_is_short_castle(cur->move))
return cur->move;
return MOVE_NONE;
}
// Normal moves. We use a simple FSM to parse the san string
for (size_t i = 0; i < movestr.length(); i++)
{
char type, c = movestr[i];
if (pieceLetters.find(c) != string::npos)
type = 'P';
else if (c >= 'a' && c <= 'h')
type = 'F';
else if (c >= '1' && c <= '8')
type = 'R';
else
type = c;
switch (type) {
case 'P':
if (state == START)
{
pt = piece_type_from_char(c);
state = TO_FILE;
}
else if (state == PROMOTION)
{
promotion = piece_type_from_char(c);
state = (i < movestr.length() - 1 ? CHECK : END);
}
else
return MOVE_NONE;
break;
case 'F':
if (state == START)
{
pt = PAWN;
fromFile = toFile = file_from_char(c);
state = TO_RANK;
}
else if (state == TO_FILE)
{
toFile = file_from_char(c);
state = TO_RANK;
}
else if (state == TO_RANK && toFile != FILE_NONE)
{
// Previous file was for disambiguation
fromFile = toFile;
toFile = file_from_char(c);
}
else
return MOVE_NONE;
break;
case 'R':
if (state == TO_RANK)
{
toRank = rank_from_char(c);
state = (i < movestr.length() - 1) ? PROMOTION_OR_CHECK : END;
}
else if (state == TO_FILE && fromRank == RANK_NONE)
{
// It's a disambiguation rank instead of a file
fromRank = rank_from_char(c);
}
else
return MOVE_NONE;
break;
case 'x':
case 'X':
if (state == TO_RANK)
{
// Previous file was for disambiguation, or it's a pawn capture
fromFile = toFile;
state = TO_FILE;
}
else if (state != TO_FILE)
return MOVE_NONE;
break;
case '=':
if (state == PROMOTION_OR_CHECK)
state = PROMOTION;
else
return MOVE_NONE;
break;
case '+':
case '#':
if (state == PROMOTION_OR_CHECK || state == CHECK)
state = END;
else
return MOVE_NONE;
break;
default:
return MOVE_NONE;
break;
}
}
if (state != END)
return MOVE_NONE;
// Look for an unambiguous matching move
to = make_square(toFile, toRank);
matches = 0;
for (MoveStack* cur = mlist; cur != last; cur++)
{
from = move_from(cur->move);
if ( pos.type_of_piece_on(from) == pt
&& move_to(cur->move) == to
&& move_promotion_piece(cur->move) == promotion
&& (fromFile == FILE_NONE || fromFile == square_file(from))
&& (fromRank == RANK_NONE || fromRank == square_rank(from)))
{
move = cur->move;
matches++;
}
}
return matches == 1 ? move : MOVE_NONE;
}
/// line_to_san() takes a position and a line (an array of moves representing
/// a sequence of legal moves from the position) as input, and returns a
/// string containing the line in short algebraic notation. If the boolean
/// parameter 'breakLines' is true, line breaks are inserted, with a line
/// length of 80 characters. After a line break, 'startColumn' spaces are
/// inserted at the beginning of the new line.
const string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines) {
StateInfo st;
std::stringstream s;
string moveStr;
size_t length = 0;
size_t maxLength = 80 - startColumn;
Position p(pos, pos.thread());
for (Move* m = line; *m != MOVE_NONE; m++)
{
moveStr = move_to_san(p, *m);
length += moveStr.length() + 1;
if (breakLines && length > maxLength)
{
s << "\n" << std::setw(startColumn) << " ";
length = moveStr.length() + 1;
}
s << moveStr << ' ';
if (*m == MOVE_NULL)
p.do_null_move(st);
else
p.do_move(*m, st);
}
return s.str();
}
/// pretty_pv() creates a human-readable string from a position and a PV.
/// It is used to write search information to the log file (which is created
/// when the UCI parameter "Use Search Log" is "true").
const string pretty_pv(const Position& pos, int time, int depth,
Value score, ValueType type, Move pv[]) {
const int64_t K = 1000;
const int64_t M = 1000000;
std::stringstream s;
// Depth
s << std::setw(2) << depth << " ";
// Score
s << (type == VALUE_TYPE_LOWER ? ">" : type == VALUE_TYPE_UPPER ? "<" : " ")
<< std::setw(7) << score_string(score);
// Time
s << std::setw(8) << time_string(time) << " ";
// Nodes
if (pos.nodes_searched() < M)
s << std::setw(8) << pos.nodes_searched() / 1 << " ";
else if (pos.nodes_searched() < K * M)
s << std::setw(7) << pos.nodes_searched() / K << "K ";
else
s << std::setw(7) << pos.nodes_searched() / M << "M ";
// PV
s << line_to_san(pos, pv, 30, true);
return s.str();
}
namespace {
Ambiguity move_ambiguity(const Position& pos, Move m) {
MoveStack mlist[MOVES_MAX], *last;
Move candidates[8];
Square from = move_from(m);
Square to = move_to(m);
Piece pc = pos.piece_on(from);
int matches = 0, f = 0, r = 0;
// If there is only one piece 'pc' then move cannot be ambiguous
if (pos.piece_count(pos.side_to_move(), type_of_piece(pc)) == 1)
return AMBIGUITY_NONE;
// Collect all legal moves of piece 'pc' with destination 'to'
last = generate_moves(pos, mlist);
for (MoveStack* cur = mlist; cur != last; cur++)
if (move_to(cur->move) == to && pos.piece_on(move_from(cur->move)) == pc)
candidates[matches++] = cur->move;
if (matches == 1)
return AMBIGUITY_NONE;
for (int i = 0; i < matches; i++)
{
if (square_file(move_from(candidates[i])) == square_file(from))
f++;
if (square_rank(move_from(candidates[i])) == square_rank(from))
r++;
}
return f == 1 ? AMBIGUITY_FILE : r == 1 ? AMBIGUITY_RANK : AMBIGUITY_BOTH;
}
const string time_string(int millisecs) {
const int MSecMinute = 1000 * 60;
const int MSecHour = 1000 * 60 * 60;
std::stringstream s;
s << std::setfill('0');
int hours = millisecs / MSecHour;
int minutes = (millisecs - hours * MSecHour) / MSecMinute;
int seconds = (millisecs - hours * MSecHour - minutes * MSecMinute) / 1000;
if (hours)
s << hours << ':';
s << std::setw(2) << minutes << ':' << std::setw(2) << seconds;
return s.str();
}
const string score_string(Value v) {
std::stringstream s;
if (v >= VALUE_MATE - 200)
s << "#" << (VALUE_MATE - v + 1) / 2;
else if (v <= -VALUE_MATE + 200)
s << "-#" << (VALUE_MATE + v) / 2;
else
{
float floatScore = float(v) / float(PawnValueMidgame);
if (v >= 0)
s << '+';
s << std::setprecision(2) << std::fixed << floatScore;
}
return s.str();
}
}
-44
View File
@@ -1,44 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(SAN_H_INCLUDED)
#define SAN_H_INCLUDED
////
//// Includes
////
#include <string>
#include "move.h"
#include "position.h"
#include "value.h"
////
//// Prototypes
////
extern const std::string move_to_san(Position& pos, Move m);
extern Move move_from_san(const Position& pos, const std::string& str);
extern const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines);
extern const std::string pretty_pv(const Position& pos, int time, int depth, Value score, ValueType type, Move pv[]);
#endif // !defined(SAN_H_INCLUDED)
+865 -1367
View File
File diff suppressed because it is too large Load Diff
+24 -28
View File
@@ -17,39 +17,24 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(SEARCH_H_INCLUDED)
#define SEARCH_H_INCLUDED
////
//// Includes
////
#include <cstring>
#include "depth.h"
#include "move.h"
#include "value.h"
#include "types.h"
////
//// Constants
////
const int PLY_MAX = 100;
const int PLY_MAX_PLUS_2 = PLY_MAX + 2;
////
//// Types
////
class Position;
struct SplitPoint;
/// The SearchStack struct keeps track of the information we need to remember
/// from nodes shallower and deeper in the tree during the search. Each
/// search thread has its own array of SearchStack objects, indexed by the
/// current ply.
struct EvalInfo;
struct SplitPoint;
struct SearchStack {
int ply;
Move currentMove;
Move mateKiller;
Move excludedMove;
@@ -63,15 +48,26 @@ struct SearchStack {
};
////
//// Prototypes
////
/// The SearchLimits struct stores information sent by GUI about available time
/// to search the current move, maximum depth/time, if we are in analysis mode
/// or if we have to ponder while is our opponent's side to move.
struct SearchLimits {
SearchLimits() { memset(this, 0, sizeof(SearchLimits)); }
SearchLimits(int t, int i, int mtg, int mt, int md, int mn, bool inf, bool pon)
: time(t), increment(i), movesToGo(mtg), maxTime(mt), maxDepth(md),
maxNodes(mn), infinite(inf), ponder(pon) {}
bool useTimeManagement() const { return !(maxTime | maxDepth | maxNodes | int(infinite)); }
int time, increment, movesToGo, maxTime, maxDepth, maxNodes;
bool infinite, ponder;
};
extern void init_search();
extern void init_threads();
extern void exit_threads();
extern int perft(Position& pos, Depth depth);
extern bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[],
int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]);
extern int64_t perft(Position& pos, Depth depth);
extern bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]);
#endif // !defined(SEARCH_H_INCLUDED)
-183
View File
@@ -1,183 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(SQUARE_H_INCLUDED)
#define SQUARE_H_INCLUDED
////
//// Includes
////
#include <cstdlib> // for abs()
#include <string>
#include "color.h"
#include "misc.h"
////
//// Types
////
enum Square {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4,
SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5,
SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6,
SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7,
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
SQ_NONE
};
enum File {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NONE
};
enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NONE
};
enum SquareDelta {
DELTA_N = 8, DELTA_E = 1, DELTA_S = -8, DELTA_W = -1, DELTA_NONE = 0,
DELTA_NN = DELTA_N + DELTA_N,
DELTA_NE = DELTA_N + DELTA_E,
DELTA_SE = DELTA_S + DELTA_E,
DELTA_SS = DELTA_S + DELTA_S,
DELTA_SW = DELTA_S + DELTA_W,
DELTA_NW = DELTA_N + DELTA_W
};
ENABLE_OPERATORS_ON(Square);
ENABLE_OPERATORS_ON(File);
ENABLE_OPERATORS_ON(Rank);
ENABLE_OPERATORS_ON(SquareDelta);
////
//// Constants
////
const int FlipMask = 56;
const int FlopMask = 7;
////
//// Inline functions
////
inline Square operator+ (Square x, SquareDelta i) { return x + Square(i); }
inline void operator+= (Square& x, SquareDelta i) { x = x + Square(i); }
inline Square operator- (Square x, SquareDelta i) { return x - Square(i); }
inline void operator-= (Square& x, SquareDelta i) { x = x - Square(i); }
inline Square make_square(File f, Rank r) {
return Square(int(f) | (int(r) << 3));
}
inline File square_file(Square s) {
return File(int(s) & 7);
}
inline Rank square_rank(Square s) {
return Rank(int(s) >> 3);
}
inline Square flip_square(Square s) {
return Square(int(s) ^ FlipMask);
}
inline Square flop_square(Square s) {
return Square(int(s) ^ FlopMask);
}
inline Square relative_square(Color c, Square s) {
return Square(int(s) ^ (int(c) * FlipMask));
}
inline Rank relative_rank(Color c, Square s) {
return square_rank(relative_square(c, s));
}
inline SquareColor square_color(Square s) {
return SquareColor((int(square_file(s)) + int(square_rank(s))) & 1);
}
inline bool same_color_squares(Square s1, Square s2) {
int s = int(s1) ^ int(s2);
return (((s >> 3) ^ s) & 1) == 0;
}
inline int file_distance(File f1, File f2) {
return abs(int(f1) - int(f2));
}
inline int file_distance(Square s1, Square s2) {
return file_distance(square_file(s1), square_file(s2));
}
inline int rank_distance(Rank r1, Rank r2) {
return abs(int(r1) - int(r2));
}
inline int rank_distance(Square s1, Square s2) {
return rank_distance(square_rank(s1), square_rank(s2));
}
inline int square_distance(Square s1, Square s2) {
return Max(file_distance(s1, s2), rank_distance(s1, s2));
}
inline File file_from_char(char c) {
return File(c - 'a') + FILE_A;
}
inline char file_to_char(File f) {
return char(f - FILE_A + int('a'));
}
inline Rank rank_from_char(char c) {
return Rank(c - '1') + RANK_1;
}
inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('1'));
}
inline const std::string square_to_string(Square s) {
return std::string(1, file_to_char(square_file(s)))
+ std::string(1, rank_to_char(square_rank(s)));
}
inline bool file_is_ok(File f) {
return f >= FILE_A && f <= FILE_H;
}
inline bool rank_is_ok(Rank r) {
return r >= RANK_1 && r <= RANK_8;
}
inline bool square_is_ok(Square s) {
return file_is_ok(square_file(s)) && rank_is_ok(square_rank(s));
}
#endif // !defined(SQUARE_H_INCLUDED)
+345
View File
@@ -0,0 +1,345 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include "thread.h"
#include "ucioption.h"
ThreadsManager Threads; // Global object definition
namespace { extern "C" {
// start_routine() is the C function which is called when a new thread
// is launched. It simply calls idle_loop() with the supplied threadID.
// There are two versions of this function; one for POSIX threads and
// one for Windows threads.
#if defined(_MSC_VER)
DWORD WINAPI start_routine(LPVOID threadID) {
Threads.idle_loop(*(int*)threadID, NULL);
return 0;
}
#else
void* start_routine(void* threadID) {
Threads.idle_loop(*(int*)threadID, NULL);
return NULL;
}
#endif
} }
// wake_up() wakes up the thread, normally at the beginning of the search or,
// if "sleeping threads" is used, when there is some work to do.
void Thread::wake_up() {
lock_grab(&sleepLock);
cond_signal(&sleepCond);
lock_release(&sleepLock);
}
// cutoff_occurred() checks whether a beta cutoff has occurred in
// the thread's currently active split point, or in some ancestor of
// the current split point.
bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = splitPoint; sp; sp = sp->parent)
if (sp->is_betaCutoff)
return true;
return false;
}
// is_available_to() checks whether the thread is available to help the thread with
// threadID "master" at a split point. An obvious requirement is that thread must be
// idle. With more than two threads, this is not by itself sufficient: If the thread
// is the master of some active split point, it is only available as a slave to the
// threads which are busy searching the split point at the top of "slave"'s split
// point stack (the "helpful master concept" in YBWC terminology).
bool Thread::is_available_to(int master) const {
if (state != AVAILABLE)
return false;
// Make a local copy to be sure doesn't become zero under our feet while
// testing next condition and so leading to an out of bound access.
int localActiveSplitPoints = activeSplitPoints;
// No active split points means that the thread is available as a slave for any
// other thread otherwise apply the "helpful master" concept if possible.
if ( !localActiveSplitPoints
|| splitPoints[localActiveSplitPoints - 1].is_slave[master])
return true;
return false;
}
// read_uci_options() updates number of active threads and other internal
// parameters according to the UCI options values. It is called before
// to start a new search.
void ThreadsManager::read_uci_options() {
maxThreadsPerSplitPoint = Options["Maximum Number of Threads per Split Point"].value<int>();
minimumSplitDepth = Options["Minimum Split Depth"].value<int>() * ONE_PLY;
useSleepingThreads = Options["Use Sleeping Threads"].value<bool>();
activeThreads = Options["Threads"].value<int>();
}
// init() is called during startup. Initializes locks and condition variables
// and launches all threads sending them immediately to sleep.
void ThreadsManager::init() {
int threadID[MAX_THREADS];
// This flag is needed to properly end the threads when program exits
allThreadsShouldExit = false;
// Threads will sent to sleep as soon as created, only main thread is kept alive
activeThreads = 1;
threads[0].state = Thread::SEARCHING;
// Allocate pawn and material hash tables for main thread
init_hash_tables();
lock_init(&mpLock);
// Initialize thread and split point locks
for (int i = 0; i < MAX_THREADS; i++)
{
lock_init(&threads[i].sleepLock);
cond_init(&threads[i].sleepCond);
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_init(&(threads[i].splitPoints[j].lock));
}
// Create and startup all the threads but the main that is already running
for (int i = 1; i < MAX_THREADS; i++)
{
threads[i].state = Thread::INITIALIZING;
threadID[i] = i;
#if defined(_MSC_VER)
bool ok = (CreateThread(NULL, 0, start_routine, (LPVOID)&threadID[i], 0, NULL) != NULL);
#else
pthread_t pthreadID;
bool ok = (pthread_create(&pthreadID, NULL, start_routine, (void*)&threadID[i]) == 0);
pthread_detach(pthreadID);
#endif
if (!ok)
{
std::cout << "Failed to create thread number " << i << std::endl;
::exit(EXIT_FAILURE);
}
// Wait until the thread has finished launching and is gone to sleep
while (threads[i].state == Thread::INITIALIZING) {}
}
}
// exit() is called to cleanly exit the threads when the program finishes
void ThreadsManager::exit() {
// Force the woken up threads to exit idle_loop() and hence terminate
allThreadsShouldExit = true;
for (int i = 0; i < MAX_THREADS; i++)
{
// Wake up all the threads and waits for termination
if (i != 0)
{
threads[i].wake_up();
while (threads[i].state != Thread::TERMINATED) {}
}
// Now we can safely destroy the locks and wait conditions
lock_destroy(&threads[i].sleepLock);
cond_destroy(&threads[i].sleepCond);
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_destroy(&(threads[i].splitPoints[j].lock));
}
lock_destroy(&mpLock);
}
// init_hash_tables() dynamically allocates pawn and material hash tables
// according to the number of active threads. This avoids preallocating
// memory for all possible threads if only few are used as, for instance,
// on mobile devices where memory is scarce and allocating for MAX_THREADS
// threads could even result in a crash.
void ThreadsManager::init_hash_tables() {
for (int i = 0; i < activeThreads; i++)
{
threads[i].pawnTable.init();
threads[i].materialTable.init();
}
}
// available_slave_exists() tries to find an idle thread which is available as
// a slave for the thread with threadID "master".
bool ThreadsManager::available_slave_exists(int master) const {
assert(master >= 0 && master < activeThreads);
for (int i = 0; i < activeThreads; i++)
if (i != master && threads[i].is_available_to(master))
return true;
return false;
}
// split() does the actual work of distributing the work at a node between
// several available threads. If it does not succeed in splitting the
// node (because no idle threads are available, or because we have no unused
// split point objects), the function immediately returns. If splitting is
// possible, a SplitPoint object is initialized with all the data that must be
// copied to the helper threads and we tell our helper threads that they have
// been assigned work. This will cause them to instantly leave their idle loops and
// call search().When all threads have returned from search() then split() returns.
template <bool Fake>
void ThreadsManager::split(Position& pos, SearchStack* ss, Value* alpha, const Value beta,
Value* bestValue, Depth depth, Move threatMove,
int moveCount, MovePicker* mp, bool pvNode) {
assert(pos.is_ok());
assert(*bestValue >= -VALUE_INFINITE);
assert(*bestValue <= *alpha);
assert(*alpha < beta);
assert(beta <= VALUE_INFINITE);
assert(depth > DEPTH_ZERO);
assert(pos.thread() >= 0 && pos.thread() < activeThreads);
assert(activeThreads > 1);
int i, master = pos.thread();
Thread& masterThread = threads[master];
lock_grab(&mpLock);
// If no other thread is available to help us, or if we have too many
// active split points, don't split.
if ( !available_slave_exists(master)
|| masterThread.activeSplitPoints >= MAX_ACTIVE_SPLIT_POINTS)
{
lock_release(&mpLock);
return;
}
// Pick the next available split point object from the split point stack
SplitPoint& splitPoint = masterThread.splitPoints[masterThread.activeSplitPoints++];
// Initialize the split point object
splitPoint.parent = masterThread.splitPoint;
splitPoint.master = master;
splitPoint.is_betaCutoff = false;
splitPoint.depth = depth;
splitPoint.threatMove = threatMove;
splitPoint.alpha = *alpha;
splitPoint.beta = beta;
splitPoint.pvNode = pvNode;
splitPoint.bestValue = *bestValue;
splitPoint.mp = mp;
splitPoint.moveCount = moveCount;
splitPoint.pos = &pos;
splitPoint.nodes = 0;
splitPoint.ss = ss;
for (i = 0; i < activeThreads; i++)
splitPoint.is_slave[i] = false;
masterThread.splitPoint = &splitPoint;
// If we are here it means we are not available
assert(masterThread.state != Thread::AVAILABLE);
int workersCnt = 1; // At least the master is included
// Allocate available threads setting state to THREAD_BOOKED
for (i = 0; !Fake && i < activeThreads && workersCnt < maxThreadsPerSplitPoint; i++)
if (i != master && threads[i].is_available_to(master))
{
threads[i].state = Thread::BOOKED;
threads[i].splitPoint = &splitPoint;
splitPoint.is_slave[i] = true;
workersCnt++;
}
assert(Fake || workersCnt > 1);
// We can release the lock because slave threads are already booked and master is not available
lock_release(&mpLock);
// Tell the threads that they have work to do. This will make them leave
// their idle loop.
for (i = 0; i < activeThreads; i++)
if (i == master || splitPoint.is_slave[i])
{
assert(i == master || threads[i].state == Thread::BOOKED);
threads[i].state = Thread::WORKISWAITING; // This makes the slave to exit from idle_loop()
if (useSleepingThreads && i != master)
threads[i].wake_up();
}
// Everything is set up. The master thread enters the idle loop, from
// which it will instantly launch a search, because its state is
// THREAD_WORKISWAITING. We send the split point as a second parameter to the
// idle loop, which means that the main thread will return from the idle
// loop when all threads have finished their work at this split point.
idle_loop(master, &splitPoint);
// We have returned from the idle loop, which means that all threads are
// finished. Update alpha and bestValue, and return.
lock_grab(&mpLock);
*alpha = splitPoint.alpha;
*bestValue = splitPoint.bestValue;
masterThread.activeSplitPoints--;
masterThread.splitPoint = splitPoint.parent;
pos.set_nodes_searched(pos.nodes_searched() + splitPoint.nodes);
lock_release(&mpLock);
}
// Explicit template instantiations
template void ThreadsManager::split<false>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool);
template void ThreadsManager::split<true>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool);
+69 -33
View File
@@ -17,51 +17,35 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(THREAD_H_INCLUDED)
#define THREAD_H_INCLUDED
////
//// Includes
////
#include <cstring>
#include "lock.h"
#include "material.h"
#include "movepick.h"
#include "pawns.h"
#include "position.h"
#include "search.h"
////
//// Constants and variables
////
const int MAX_THREADS = 16;
const int MAX_THREADS = 32;
const int MAX_ACTIVE_SPLIT_POINTS = 8;
////
//// Types
////
struct SplitPoint {
// Const data after splitPoint has been setup
SplitPoint* parent;
const Position* pos;
Depth depth;
bool pvNode, mateThreat;
bool pvNode;
Value beta;
int ply;
int master;
Move threatMove;
SearchStack sstack[MAX_THREADS][PLY_MAX_PLUS_2];
// Const pointers to shared data
MovePicker* mp;
SearchStack* parentSstack;
SearchStack* ss;
// Shared data
Lock lock;
@@ -69,23 +53,37 @@ struct SplitPoint {
volatile Value alpha;
volatile Value bestValue;
volatile int moveCount;
volatile bool betaCutoff;
volatile int slaves[MAX_THREADS];
volatile bool is_betaCutoff;
volatile bool is_slave[MAX_THREADS];
};
// ThreadState type is used to represent thread's current state
enum ThreadState
{
THREAD_INITIALIZING, // thread is initializing itself
THREAD_SEARCHING, // thread is performing work
THREAD_AVAILABLE, // thread is waiting for work
THREAD_BOOKED, // other thread (master) has booked us as a slave
THREAD_WORKISWAITING, // master has ordered us to start
THREAD_TERMINATED // we are quitting and thread is terminated
};
/// Thread struct is used to keep together all the thread related stuff like locks,
/// state and especially split points. We also use per-thread pawn and material hash
/// tables so that once we get a pointer to an entry its life time is unlimited and
/// we don't have to care about someone changing the entry under our feet.
struct Thread {
enum ThreadState
{
INITIALIZING, // Thread is initializing itself
SEARCHING, // Thread is performing work
AVAILABLE, // Thread is waiting for work
BOOKED, // Other thread (master) has booked us as a slave
WORKISWAITING, // Master has ordered us to start
TERMINATED // We are quitting and thread is terminated
};
void wake_up();
bool cutoff_occurred() const;
bool is_available_to(int master) const;
MaterialInfoTable materialTable;
PawnInfoTable pawnTable;
int maxPly;
Lock sleepLock;
WaitCondition sleepCond;
volatile ThreadState state;
SplitPoint* volatile splitPoint;
volatile int activeSplitPoints;
@@ -93,4 +91,42 @@ struct Thread {
};
/// ThreadsManager class is used to handle all the threads related stuff like init,
/// starting, parking and, the most important, launching a slave thread at a split
/// point. All the access to shared thread data is done through this class.
class ThreadsManager {
/* As long as the single ThreadsManager object is defined as a global we don't
need to explicitly initialize to zero its data members because variables with
static storage duration are automatically set to zero before enter main()
*/
public:
Thread& operator[](int threadID) { return threads[threadID]; }
void init();
void exit();
void init_hash_tables();
int min_split_depth() const { return minimumSplitDepth; }
int size() const { return activeThreads; }
void set_size(int cnt) { activeThreads = cnt; }
void read_uci_options();
bool available_slave_exists(int master) const;
void idle_loop(int threadID, SplitPoint* sp);
template <bool Fake>
void split(Position& pos, SearchStack* ss, Value* alpha, const Value beta, Value* bestValue,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, bool pvNode);
private:
Lock mpLock;
Depth minimumSplitDepth;
int maxThreadsPerSplitPoint;
bool useSleepingThreads;
int activeThreads;
volatile bool allThreadsShouldExit;
Thread threads[MAX_THREADS];
};
extern ThreadsManager Threads;
#endif // !defined(THREAD_H_INCLUDED)
+19 -28
View File
@@ -17,21 +17,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cmath>
#include "misc.h"
#include "search.h"
#include "timeman.h"
#include "ucioption.h"
////
//// Local definitions
////
namespace {
/// Constants
@@ -84,31 +76,28 @@ namespace {
}
////
//// Functions
////
void TimeManager::pv_instability(int curChanges, int prevChanges) {
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ prevChanges * (optimumSearchTime / 3);
}
void TimeManager::init(int myTime, int myInc, int movesToGo, int currentPly)
void TimeManager::init(const SearchLimits& limits, int currentPly)
{
/* We support four different kind of time controls:
Inc == 0 && movesToGo == 0 means: x basetime [sudden death!]
Inc == 0 && movesToGo != 0 means: (x moves) / (y minutes)
Inc > 0 && movesToGo == 0 means: x basetime + z inc.
Inc > 0 && movesToGo != 0 means: (x moves) / (y minutes) + z inc
increment == 0 && movesToGo == 0 means: x basetime [sudden death!]
increment == 0 && movesToGo != 0 means: x moves in y minutes
increment > 0 && movesToGo == 0 means: x basetime + z increment
increment > 0 && movesToGo != 0 means: x moves in y minutes + z increment
Time management is adjusted by following UCI parameters:
emergencyMoveHorizon :Be prepared to always play at least this many moves
emergencyBaseTime :Always attempt to keep at least this much time (in ms) at clock
emergencyMoveTime :Plus attempt to keep at least this much time for each remaining emergency move
minThinkingTime :No matter what, use at least this much thinking before doing the move
emergencyMoveHorizon: Be prepared to always play at least this many moves
emergencyBaseTime : Always attempt to keep at least this much time (in ms) at clock
emergencyMoveTime : Plus attempt to keep at least this much time for each remaining emergency move
minThinkingTime : No matter what, use at least this much thinking before doing the move
*/
int hypMTG, hypMyTime, t1, t2;
@@ -121,14 +110,19 @@ void TimeManager::init(int myTime, int myInc, int movesToGo, int currentPly)
// Initialize to maximum values but unstablePVExtraTime that is reset
unstablePVExtraTime = 0;
optimumSearchTime = maximumSearchTime = myTime;
optimumSearchTime = maximumSearchTime = limits.time;
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
for (hypMTG = 1; hypMTG <= (movesToGo ? Min(movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
for (hypMTG = 1; hypMTG <= (limits.movesToGo ? Min(limits.movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
{
// Calculate thinking time for hypothetic "moves to go"-value
hypMyTime = Max(myTime + (hypMTG - 1) * myInc - emergencyBaseTime - Min(hypMTG, emergencyMoveHorizon) * emergencyMoveTime, 0);
hypMyTime = limits.time
+ limits.increment * (hypMTG - 1)
- emergencyBaseTime
- emergencyMoveTime * Min(hypMTG, emergencyMoveHorizon);
hypMyTime = Max(hypMyTime, 0);
t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly);
@@ -144,9 +138,6 @@ void TimeManager::init(int myTime, int myInc, int movesToGo, int currentPly)
optimumSearchTime = Min(optimumSearchTime, maximumSearchTime);
}
////
//// Local functions
////
namespace {
+2 -5
View File
@@ -17,18 +17,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(TIMEMAN_H_INCLUDED)
#define TIMEMAN_H_INCLUDED
////
//// Prototypes
////
struct SearchLimits;
class TimeManager {
public:
void init(int myTime, int myInc, int movesToGo, int currentPly);
void init(const SearchLimits& limits, int currentPly);
void pv_instability(int curChanges, int prevChanges);
int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
int maximum_time() const { return maximumSearchTime; }
+37 -51
View File
@@ -17,28 +17,18 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include <cstring>
#include <iostream>
#include "tt.h"
// The main transposition table
TranspositionTable TT;
////
//// Functions
////
TranspositionTable TT; // Our global transposition table
TranspositionTable::TranspositionTable() {
size = 0;
entries = 0;
generation = 0;
size = generation = 0;
entries = NULL;
}
TranspositionTable::~TranspositionTable() {
@@ -47,40 +37,39 @@ TranspositionTable::~TranspositionTable() {
}
/// TranspositionTable::set_size sets the size of the transposition table,
/// TranspositionTable::set_size() sets the size of the transposition table,
/// measured in megabytes.
void TranspositionTable::set_size(size_t mbSize) {
size_t newSize = 1024;
// Transposition table consists of clusters and
// each cluster consists of ClusterSize number of TTEntries.
// Each non-empty entry contains information of exactly one position.
// newSize is the number of clusters we are going to allocate.
while ((2 * newSize) * sizeof(TTCluster) <= (mbSize << 20))
// Transposition table consists of clusters and each cluster consists
// of ClusterSize number of TTEntries. Each non-empty entry contains
// information of exactly one position and newSize is the number of
// clusters we are going to allocate.
while (2ULL * newSize * sizeof(TTCluster) <= (mbSize << 20))
newSize *= 2;
if (newSize != size)
if (newSize == size)
return;
size = newSize;
delete [] entries;
entries = new (std::nothrow) TTCluster[size];
if (!entries)
{
size = newSize;
delete [] entries;
entries = new TTCluster[size];
if (!entries)
{
std::cerr << "Failed to allocate " << mbSize
<< " MB for transposition table." << std::endl;
exit(EXIT_FAILURE);
}
clear();
std::cerr << "Failed to allocate " << mbSize
<< " MB for transposition table." << std::endl;
exit(EXIT_FAILURE);
}
clear();
}
/// TranspositionTable::clear overwrites the entire transposition table
/// TranspositionTable::clear() overwrites the entire transposition table
/// with zeroes. It is called whenever the table is resized, or when the
/// user asks the program to clear the table (from the UCI interface).
/// Perhaps we should also clear it when the "ucinewgame" command is received?
void TranspositionTable::clear() {
@@ -88,26 +77,25 @@ void TranspositionTable::clear() {
}
/// TranspositionTable::store writes a new entry containing position key and
/// valuable information of current position.
/// The Lowest order bits of position key are used to decide on which cluster
/// the position will be placed.
/// TranspositionTable::store() writes a new entry containing position key and
/// valuable information of current position. The lowest order bits of position
/// key are used to decide on which cluster the position will be placed.
/// When a new entry is written and there are no empty entries available in cluster,
/// it replaces the least valuable of entries.
/// A TTEntry t1 is considered to be more valuable than a TTEntry t2 if t1 is from the
/// current search and t2 is from a previous search, or if the depth of t1
/// is bigger than the depth of t2.
/// it replaces the least valuable of entries. A TTEntry t1 is considered to be
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
/// a previous search, or if the depth of t1 is bigger than the depth of t2.
void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m, Value statV, Value kingD) {
int c1, c2, c3;
TTEntry *tte, *replace;
uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key
uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key inside the cluster
tte = replace = first_entry(posKey);
for (int i = 0; i < ClusterSize; i++, tte++)
{
if (!tte->key() || tte->key() == posKey32) // empty or overwrite old
if (!tte->key() || tte->key() == posKey32) // Empty or overwrite old
{
// Preserve any existing ttMove
if (m == MOVE_NONE)
@@ -117,11 +105,9 @@ void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d,
return;
}
if (i == 0) // Replacing first entry is default and already set before entering for-loop
continue;
// Implement replace strategy
c1 = (replace->generation() == generation ? 2 : 0);
c2 = (tte->generation() == generation ? -2 : 0);
c2 = (tte->generation() == generation || tte->type() == VALUE_TYPE_EXACT ? -2 : 0);
c3 = (tte->depth() < replace->depth() ? 1 : 0);
if (c1 + c2 + c3 > 0)
@@ -131,11 +117,11 @@ void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d,
}
/// TranspositionTable::retrieve looks up the current position in the
/// transposition table. Returns a pointer to the TTEntry or NULL
/// if position is not found.
/// TranspositionTable::probe() looks up the current position in the
/// transposition table. Returns a pointer to the TTEntry or NULL if
/// position is not found.
TTEntry* TranspositionTable::retrieve(const Key posKey) const {
TTEntry* TranspositionTable::probe(const Key posKey) const {
uint32_t posKey32 = posKey >> 32;
TTEntry* tte = first_entry(posKey);
+74 -48
View File
@@ -17,24 +17,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(TT_H_INCLUDED)
#define TT_H_INCLUDED
////
//// Includes
////
#include <iostream>
#include "depth.h"
#include "move.h"
#include "value.h"
#include "types.h"
////
//// Types
////
/// The TTEntry class is the class of transposition table entries
/// The TTEntry is the class of transposition table entries
///
/// A TTEntry needs 128 bits to be stored
///
@@ -47,59 +39,59 @@
///
/// the 32 bits of the data field are so defined
///
/// bit 0-16: move
/// bit 17-20: not used
/// bit 0-15: move
/// bit 16-20: not used
/// bit 21-22: value type
/// bit 23-31: generation
class TTEntry {
public:
void save(uint32_t k, Value v, ValueType t, Depth d, Move m, int g, Value statV, Value kd) {
void save(uint32_t k, Value v, ValueType t, Depth d, Move m, int g, Value statV, Value statM) {
key32 = k;
data = (m & 0x1FFFF) | (t << 21) | (g << 23);
value16 = int16_t(v);
depth16 = int16_t(d);
staticValue = int16_t(statV);
staticValueMargin = int16_t(kd);
key32 = (uint32_t)k;
move16 = (uint16_t)m;
valueType = (uint8_t)t;
generation8 = (uint8_t)g;
value16 = (int16_t)v;
depth16 = (int16_t)d;
staticValue = (int16_t)statV;
staticMargin = (int16_t)statM;
}
void set_generation(int g) { data = move() | (type() << 21) | (g << 23); }
void set_generation(int g) { generation8 = (uint8_t)g; }
uint32_t key() const { return key32; }
Depth depth() const { return Depth(depth16); }
Move move() const { return Move(data & 0x1FFFF); }
Value value() const { return Value(value16); }
ValueType type() const { return ValueType((data >> 21) & 3); }
int generation() const { return data >> 23; }
Value static_value() const { return Value(staticValue); }
Value static_value_margin() const { return Value(staticValueMargin); }
uint32_t key() const { return key32; }
Depth depth() const { return (Depth)depth16; }
Move move() const { return (Move)move16; }
Value value() const { return (Value)value16; }
ValueType type() const { return (ValueType)valueType; }
int generation() const { return (int)generation8; }
Value static_value() const { return (Value)staticValue; }
Value static_value_margin() const { return (Value)staticMargin; }
private:
uint32_t key32;
uint32_t data;
int16_t value16;
int16_t depth16;
int16_t staticValue;
int16_t staticValueMargin;
uint16_t move16;
uint8_t valueType, generation8;
int16_t value16, depth16, staticValue, staticMargin;
};
/// This is the number of TTEntry slots for each cluster
const int ClusterSize = 4;
/// TTCluster consists of ClusterSize number of TTEntries.
/// Size of TTCluster must not be bigger than a cache line size.
/// In case it is less, it should be padded to guarantee always aligned accesses.
/// TTCluster consists of ClusterSize number of TTEntries. Size of TTCluster
/// must not be bigger than a cache line size. In case it is less, it should
/// be padded to guarantee always aligned accesses.
struct TTCluster {
TTEntry data[ClusterSize];
};
/// The transposition table class. This is basically just a huge array
/// containing TTCluster objects, and a few methods for writing new entries
/// and reading new ones.
/// The transposition table class. This is basically just a huge array containing
/// TTCluster objects, and a few methods for writing and reading entries.
class TranspositionTable {
@@ -112,7 +104,7 @@ public:
void set_size(size_t mbSize);
void clear();
void store(const Key posKey, Value v, ValueType type, Depth d, Move m, Value statV, Value kingD);
TTEntry* retrieve(const Key posKey) const;
TTEntry* probe(const Key posKey) const;
void new_search();
TTEntry* first_entry(const Key posKey) const;
void refresh(const TTEntry* tte) const;
@@ -120,28 +112,62 @@ public:
private:
size_t size;
TTCluster* entries;
uint8_t generation; // To properly compare, size must be smaller then TT stored value
uint8_t generation; // Size must be not bigger then TTEntry::generation8
};
extern TranspositionTable TT;
/// TranspositionTable::first_entry returns a pointer to the first
/// entry of a cluster given a position. The lowest order bits of the key
/// are used to get the index of the cluster.
/// TranspositionTable::first_entry() returns a pointer to the first entry of
/// a cluster given a position. The lowest order bits of the key are used to
/// get the index of the cluster.
inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
return entries[uint32_t(posKey) & (size - 1)].data;
return entries[((uint32_t)posKey) & (size - 1)].data;
}
/// TranspositionTable::refresh updates the 'generation' value of the TTEntry
/// to avoid aging. Normally called after a TT hit, before to return.
/// TranspositionTable::refresh() updates the 'generation' value of the TTEntry
/// to avoid aging. Normally called after a TT hit.
inline void TranspositionTable::refresh(const TTEntry* tte) const {
const_cast<TTEntry*>(tte)->set_generation(generation);
}
/// A simple fixed size hash table used to store pawns and material
/// configurations. It is basically just an array of Entry objects.
/// Without cluster concept or overwrite policy.
template<class Entry, int HashSize>
struct SimpleHash {
typedef SimpleHash<Entry, HashSize> Base;
void init() {
if (entries)
return;
entries = new (std::nothrow) Entry[HashSize];
if (!entries)
{
std::cerr << "Failed to allocate " << HashSize * sizeof(Entry)
<< " bytes for hash table." << std::endl;
exit(EXIT_FAILURE);
}
memset(entries, 0, HashSize * sizeof(Entry));
}
virtual ~SimpleHash() { delete [] entries; }
Entry* probe(Key key) const { return entries + ((uint32_t)key & (HashSize - 1)); }
void prefetch(Key key) const { ::prefetch((char*)probe(key)); }
protected:
Entry* entries;
};
#endif // !defined(TT_H_INCLUDED)
+358 -64
View File
@@ -20,37 +20,34 @@
#if !defined(TYPES_H_INCLUDED)
#define TYPES_H_INCLUDED
#if !defined(_MSC_VER)
#include <climits>
#include <cstdlib>
#include <inttypes.h>
#else
#if defined(_MSC_VER)
// Disable some silly and noisy warning from MSVC compiler
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
#pragma warning(disable: 4127) // Conditional expression is constant
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16;
// MSVC does not support <inttypes.h>
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef __int16 int16_t;
typedef __int64 int64_t;
#else
#endif // !defined(_MSC_VER)
#include <inttypes.h>
// Hash keys
typedef uint64_t Key;
#endif
// Bitboard type
typedef uint64_t Bitboard;
#include <cstdlib>
#define Min(x, y) (((x) < (y)) ? (x) : (y))
#define Max(x, y) (((x) < (y)) ? (y) : (x))
////
//// Configuration
@@ -70,18 +67,26 @@ typedef uint64_t Bitboard;
//// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction.
//// | Works only in 64-bit mode. For compiling requires hardware
//// | with popcnt support. Around 4% speed-up.
////
//// -DOLD_LOCKS | By default under Windows are used the fast Slim Reader/Writer (SRW)
//// | Locks and Condition Variables: these are not supported by Windows XP
//// | and older, to compile for those platforms you should enable OLD_LOCKS.
// Automatic detection for 64-bit under Windows
#if defined(_WIN64)
#define IS_64BIT
#endif
// Automatic detection for use of bsfq asm-instruction under Windows.
// Works only in 64-bit mode. Does not work with MSVC.
#if defined(_WIN64) && defined(__INTEL_COMPILER)
// Automatic detection for use of bsfq asm-instruction under Windows
#if defined(_WIN64)
#define USE_BSFQ
#endif
// Intel header for _mm_popcnt_u64() intrinsic
#if defined(USE_POPCNT) && defined(_MSC_VER) && defined(__INTEL_COMPILER)
#include <nmmintrin.h>
#endif
// Cache line alignment specification
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
#define CACHE_LINE_ALIGNMENT __declspec(align(64))
@@ -113,64 +118,353 @@ inline void __cpuid(int CPUInfo[4], int)
}
#endif
// Define FORCE_INLINE macro to force inlining overriding compiler choice
#if defined(_MSC_VER)
#define FORCE_INLINE __forceinline
#elif defined(__GNUC__)
#define FORCE_INLINE inline __attribute__((always_inline))
#else
#define FORCE_INLINE inline
#endif
// Templetized operators used by enum types like Depth, Piece, Square and so on.
// We don't want to write the same inline for each different enum. Note that we
// pass by value to silence scaring warnings when using volatiles.
// Because these templates override common operators and are included in all the
// files, there is a possibility that the compiler silently performs some unwanted
// overrides. To avoid possible very nasty bugs the templates are disabled by default
// and must be enabled for each type on a case by case base. The enabling trick
// uses template specialization, namely we just declare following struct.
template<typename T> struct TempletizedOperator;
/// cpu_has_popcnt() detects support for popcnt instruction at runtime
inline bool cpu_has_popcnt() {
// Then to enable the enum type we use following macro that defines a specialization
// of TempletizedOperator for the given enum T. Here is defined typedef Not_Enabled.
// Name of typedef is chosen to produce somewhat informative compile error messages.
#define ENABLE_OPERATORS_ON(T) \
template<> struct TempletizedOperator<T> { typedef T Not_Enabled; }
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0x00000001);
return (CPUInfo[2] >> 23) & 1;
}
// Finally we use macro OK(T) to check if type T is enabled. The macro simply
// tries to use Not_Enabled, if was not previously defined a compile error occurs.
// The check is done fully at compile time and there is zero overhead at runtime.
#define OK(T) typedef typename TempletizedOperator<T>::Not_Enabled Type
/// CpuHasPOPCNT is a global constant initialized at startup that
/// is set to true if CPU on which application runs supports popcnt
/// hardware instruction. Unless USE_POPCNT is not defined.
#if defined(USE_POPCNT)
const bool CpuHasPOPCNT = cpu_has_popcnt();
#else
const bool CpuHasPOPCNT = false;
#endif
template<typename T>
inline T operator+ (const T d1, const T d2) { OK(T); return T(int(d1) + int(d2)); }
template<typename T>
inline T operator- (const T d1, const T d2) { OK(T); return T(int(d1) - int(d2)); }
/// CpuIs64Bit is a global constant initialized at compile time that
/// is set to true if CPU on which application runs is a 64 bits.
#if defined(IS_64BIT)
const bool CpuIs64Bit = true;
#else
const bool CpuIs64Bit = false;
#endif
template<typename T>
inline T operator* (int i, const T d) { OK(T); return T(i * int(d)); }
#include <string>
template<typename T>
inline T operator* (const T d, int i) { OK(T); return T(int(d) * i); }
typedef uint64_t Key;
typedef uint64_t Bitboard;
template<typename T>
inline T operator/ (const T d, int i) { OK(T); return T(int(d) / i); }
const int PLY_MAX = 100;
const int PLY_MAX_PLUS_2 = PLY_MAX + 2;
template<typename T>
inline T operator- (const T d) { OK(T); return T(-int(d)); }
enum ValueType {
VALUE_TYPE_NONE = 0,
VALUE_TYPE_UPPER = 1,
VALUE_TYPE_LOWER = 2,
VALUE_TYPE_EXACT = VALUE_TYPE_UPPER | VALUE_TYPE_LOWER
};
template<typename T>
inline T operator++ (T& d, int) { OK(T); d = T(int(d) + 1); return d; }
enum Value {
VALUE_ZERO = 0,
VALUE_DRAW = 0,
VALUE_KNOWN_WIN = 15000,
VALUE_MATE = 30000,
VALUE_INFINITE = 30001,
VALUE_NONE = 30002,
template<typename T>
inline T operator-- (T& d, int) { OK(T); d = T(int(d) - 1); return d; }
VALUE_MATE_IN_PLY_MAX = VALUE_MATE - PLY_MAX,
VALUE_MATED_IN_PLY_MAX = -VALUE_MATE + PLY_MAX,
template<typename T>
inline void operator+= (T& d1, const T d2) { OK(T); d1 = d1 + d2; }
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
template<typename T>
inline void operator-= (T& d1, const T d2) { OK(T); d1 = d1 - d2; }
enum PieceType {
PIECE_TYPE_NONE = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
};
template<typename T>
inline void operator*= (T& d, int i) { OK(T); d = T(int(d) * i); }
enum Piece {
PIECE_NONE_DARK_SQ = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6,
BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14, PIECE_NONE = 16
};
template<typename T>
inline void operator/= (T& d, int i) { OK(T); d = T(int(d) / i); }
enum Color {
WHITE, BLACK, COLOR_NONE
};
#undef OK
enum Depth {
ONE_PLY = 2,
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY
};
enum Square {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4,
SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5,
SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6,
SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7,
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
SQ_NONE,
DELTA_N = 8,
DELTA_E = 1,
DELTA_S = -8,
DELTA_W = -1,
DELTA_NN = DELTA_N + DELTA_N,
DELTA_NE = DELTA_N + DELTA_E,
DELTA_SE = DELTA_S + DELTA_E,
DELTA_SS = DELTA_S + DELTA_S,
DELTA_SW = DELTA_S + DELTA_W,
DELTA_NW = DELTA_N + DELTA_W
};
enum File {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H
};
enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8
};
enum SquareColor {
DARK, LIGHT
};
enum ScaleFactor {
SCALE_FACTOR_ZERO = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
/// Score enum keeps a midgame and an endgame value in a single
/// integer (enum), first LSB 16 bits are used to store endgame
/// value, while upper bits are used for midgame value. Compiler
/// is free to choose the enum type as long as can keep its data,
/// so ensure Score to be an integer type.
enum Score {
SCORE_ZERO = 0,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
#define ENABLE_OPERATORS_ON(T) \
inline T operator+ (const T d1, const T d2) { return T(int(d1) + int(d2)); } \
inline T operator- (const T d1, const T d2) { return T(int(d1) - int(d2)); } \
inline T operator* (int i, const T d) { return T(i * int(d)); } \
inline T operator* (const T d, int i) { return T(int(d) * i); } \
inline T operator/ (const T d, int i) { return T(int(d) / i); } \
inline T operator- (const T d) { return T(-int(d)); } \
inline T operator++ (T& d, int) {d = T(int(d) + 1); return d; } \
inline T operator-- (T& d, int) { d = T(int(d) - 1); return d; } \
inline void operator+= (T& d1, const T d2) { d1 = d1 + d2; } \
inline void operator-= (T& d1, const T d2) { d1 = d1 - d2; } \
inline void operator*= (T& d, int i) { d = T(int(d) * i); } \
inline void operator/= (T& d, int i) { d = T(int(d) / i); }
ENABLE_OPERATORS_ON(Value)
ENABLE_OPERATORS_ON(PieceType)
ENABLE_OPERATORS_ON(Piece)
ENABLE_OPERATORS_ON(Color)
ENABLE_OPERATORS_ON(Depth)
ENABLE_OPERATORS_ON(Square)
ENABLE_OPERATORS_ON(File)
ENABLE_OPERATORS_ON(Rank)
#undef ENABLE_OPERATORS_ON
// Extra operators for adding integers to a Value
inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
inline Value operator- (Value v, int i) { return Value(int(v) - i); }
// Extracting the _signed_ lower and upper 16 bits it not so trivial
// because according to the standard a simple cast to short is
// implementation defined and so is a right shift of a signed integer.
inline Value mg_value(Score s) { return Value(((int(s) + 32768) & ~0xffff) / 0x10000); }
// Unfortunatly on Intel 64 bit we have a small speed regression, so use a faster code in
// this case, although not 100% standard compliant it seems to work for Intel and MSVC.
#if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER))
inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); }
#else
inline Value eg_value(Score s) { return Value((int)(unsigned(s) & 0x7fffu) - (int)(unsigned(s) & 0x8000u)); }
#endif
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
// Division must be handled separately for each term
inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); }
// Only declared but not defined. We don't want to multiply two scores due to
// a very high risk of overflow. So user should explicitly convert to integer.
inline Score operator*(Score s1, Score s2);
// Remaining Score operators are standard
inline Score operator+ (const Score d1, const Score d2) { return Score(int(d1) + int(d2)); }
inline Score operator- (const Score d1, const Score d2) { return Score(int(d1) - int(d2)); }
inline Score operator* (int i, const Score d) { return Score(i * int(d)); }
inline Score operator* (const Score d, int i) { return Score(int(d) * i); }
inline Score operator- (const Score d) { return Score(-int(d)); }
inline void operator+= (Score& d1, const Score d2) { d1 = d1 + d2; }
inline void operator-= (Score& d1, const Score d2) { d1 = d1 - d2; }
inline void operator*= (Score& d, int i) { d = Score(int(d) * i); }
inline void operator/= (Score& d, int i) { d = Score(int(d) / i); }
const Value PawnValueMidgame = Value(0x0C6);
const Value PawnValueEndgame = Value(0x102);
const Value KnightValueMidgame = Value(0x331);
const Value KnightValueEndgame = Value(0x34E);
const Value BishopValueMidgame = Value(0x344);
const Value BishopValueEndgame = Value(0x359);
const Value RookValueMidgame = Value(0x4F6);
const Value RookValueEndgame = Value(0x4FE);
const Value QueenValueMidgame = Value(0x9D9);
const Value QueenValueEndgame = Value(0x9FE);
inline Value value_mate_in(int ply) {
return VALUE_MATE - ply;
}
inline Value value_mated_in(int ply) {
return -VALUE_MATE + ply;
}
inline Piece make_piece(Color c, PieceType pt) {
return Piece((int(c) << 3) | int(pt));
}
inline PieceType type_of_piece(Piece p) {
return PieceType(int(p) & 7);
}
inline Color color_of_piece(Piece p) {
return Color(int(p) >> 3);
}
inline Color opposite_color(Color c) {
return Color(int(c) ^ 1);
}
inline bool color_is_ok(Color c) {
return c == WHITE || c == BLACK;
}
inline bool piece_type_is_ok(PieceType pt) {
return pt >= PAWN && pt <= KING;
}
inline bool piece_is_ok(Piece p) {
return piece_type_is_ok(type_of_piece(p)) && color_is_ok(color_of_piece(p));
}
inline char piece_type_to_char(PieceType pt) {
static const char ch[] = " PNBRQK";
return ch[pt];
}
inline Square make_square(File f, Rank r) {
return Square((int(r) << 3) | int(f));
}
inline File square_file(Square s) {
return File(int(s) & 7);
}
inline Rank square_rank(Square s) {
return Rank(int(s) >> 3);
}
inline Square flip_square(Square s) {
return Square(int(s) ^ 56);
}
inline Square flop_square(Square s) {
return Square(int(s) ^ 7);
}
inline Square relative_square(Color c, Square s) {
return Square(int(s) ^ (int(c) * 56));
}
inline Rank relative_rank(Color c, Rank r) {
return Rank(int(r) ^ (int(c) * 7));
}
inline Rank relative_rank(Color c, Square s) {
return relative_rank(c, square_rank(s));
}
inline SquareColor square_color(Square s) {
return SquareColor(int(square_rank(s) + s) & 1);
}
inline bool opposite_color_squares(Square s1, Square s2) {
int s = int(s1) ^ int(s2);
return ((s >> 3) ^ s) & 1;
}
inline int file_distance(Square s1, Square s2) {
return abs(square_file(s1) - square_file(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(square_rank(s1) - square_rank(s2));
}
inline int square_distance(Square s1, Square s2) {
return Max(file_distance(s1, s2), rank_distance(s1, s2));
}
inline File file_from_char(char c) {
return File(c - 'a') + FILE_A;
}
inline char file_to_char(File f) {
return char(f - FILE_A + int('a'));
}
inline Rank rank_from_char(char c) {
return Rank(c - '1') + RANK_1;
}
inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('1'));
}
inline const std::string square_to_string(Square s) {
char ch[] = { file_to_char(square_file(s)), rank_to_char(square_rank(s)), 0 };
return std::string(ch);
}
inline bool file_is_ok(File f) {
return f >= FILE_A && f <= FILE_H;
}
inline bool rank_is_ok(Rank r) {
return r >= RANK_1 && r <= RANK_8;
}
inline bool square_is_ok(Square s) {
return s >= SQ_A1 && s <= SQ_H8;
}
inline Square pawn_push(Color c) {
return c == WHITE ? DELTA_N : DELTA_S;
}
#endif // !defined(TYPES_H_INCLUDED)
+88 -145
View File
@@ -17,11 +17,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include <iostream>
#include <sstream>
@@ -30,15 +25,12 @@
#include "evaluate.h"
#include "misc.h"
#include "move.h"
#include "movegen.h"
#include "position.h"
#include "san.h"
#include "search.h"
#include "ucioption.h"
using namespace std;
namespace {
// FEN string for the initial position
@@ -48,11 +40,10 @@ namespace {
// is actually a string stream built on a given input string.
typedef istringstream UCIParser;
// Local functions
void set_option(UCIParser& uip);
void set_position(Position& pos, UCIParser& uip);
bool go(Position& pos, UCIParser& uip);
void perft(Position& pos, UCIParser& uip);
void set_option(UCIParser& up);
void set_position(Position& pos, UCIParser& up);
bool go(Position& pos, UCIParser& up);
void perft(Position& pos, UCIParser& up);
}
@@ -63,12 +54,12 @@ namespace {
bool execute_uci_command(const string& cmd) {
static Position pos(StartPositionFEN, 0); // The root position
static Position pos(StartPositionFEN, false, 0); // The root position
UCIParser up(cmd);
string token;
if (!(up >> token)) // operator>>() skips any whitespace
return true;
up >> token; // operator>>() skips any whitespace
if (token == "quit")
return false;
@@ -76,15 +67,8 @@ bool execute_uci_command(const string& cmd) {
if (token == "go")
return go(pos, up);
if (token == "uci")
{
cout << "id name " << engine_name()
<< "\nid author Tord Romstad, Marco Costalba, Joona Kiiski\n";
print_uci_options();
cout << "uciok" << endl;
}
else if (token == "ucinewgame")
pos.from_fen(StartPositionFEN);
if (token == "ucinewgame")
pos.from_fen(StartPositionFEN, false);
else if (token == "isready")
cout << "readyok" << endl;
@@ -95,30 +79,31 @@ bool execute_uci_command(const string& cmd) {
else if (token == "setoption")
set_option(up);
// The remaining commands are for debugging purposes only
else if (token == "perft")
perft(pos, up);
else if (token == "d")
pos.print();
else if (token == "flip")
{
Position p(pos, pos.thread());
pos.flipped_copy(p);
}
pos.flip();
else if (token == "eval")
{
Value evalMargin;
cout << "Incremental mg: " << mg_value(pos.value())
<< "\nIncremental eg: " << eg_value(pos.value())
<< "\nFull eval: " << evaluate(pos, evalMargin) << endl;
read_evaluation_uci_options(pos.side_to_move());
cout << trace_evaluate(pos) << endl;
}
else if (token == "key")
cout << "key: " << hex << pos.get_key()
cout << "key: " << hex << pos.get_key()
<< "\nmaterial key: " << pos.get_material_key()
<< "\npawn key: " << pos.get_pawn_key() << endl;
else if (token == "perft")
perft(pos, up);
<< "\npawn key: " << pos.get_pawn_key() << endl;
else if (token == "uci")
cout << "id name " << engine_name()
<< "\nid author " << engine_authors()
<< "\n" << Options.print_all()
<< "\nuciok" << endl;
else
cout << "Unknown command: " << cmd << endl;
@@ -126,179 +111,137 @@ bool execute_uci_command(const string& cmd) {
}
////
//// Local functions
////
namespace {
// set_position() is called when Stockfish receives the "position" UCI
// command. The input parameter is a UCIParser. It is assumed
// that this parser has consumed the first token of the UCI command
// ("position"), and is ready to read the second token ("startpos"
// or "fen", if the input is well-formed).
// set_position() is called when engine receives the "position" UCI
// command. The function sets up the position described in the given
// fen string ("fen") or the starting position ("startpos") and then
// makes the moves given in the following move list ("moves").
void set_position(Position& pos, UCIParser& up) {
string token;
string token, fen;
if (!(up >> token) || (token != "startpos" && token != "fen"))
return;
up >> token; // operator>>() skips any whitespace
if (token == "startpos")
{
pos.from_fen(StartPositionFEN);
if (!(up >> token))
return;
pos.from_fen(StartPositionFEN, false);
up >> token; // Consume "moves" token if any
}
else // fen
else if (token == "fen")
{
string fen;
while (up >> token && token != "moves")
{
fen += token;
fen += ' ';
}
pos.from_fen(fen);
fen += token + " ";
pos.from_fen(fen, Options["UCI_Chess960"].value<bool>());
}
else return;
if (token != "moves")
return;
// Parse optional move list
Move move;
StateInfo st;
// Parse move list (if any)
while (up >> token)
{
move = move_from_uci(pos, token);
pos.do_move(move, st);
if (pos.rule_50_counter() == 0)
pos.reset_game_ply();
pos.inc_startpos_ply_counter(); //FIXME: make from_fen to support this and rule50
}
// Our StateInfo st is about going out of scope so copy
// its content inside pos before it disappears.
pos.detach();
pos.do_setup_move(move_from_uci(pos, token));
}
// set_option() is called when Stockfish receives the "setoption" UCI
// command. The input parameter is a UCIParser. It is assumed
// that this parser has consumed the first token of the UCI command
// ("setoption"), and is ready to read the second token ("name", if
// the input is well-formed).
// set_option() is called when engine receives the "setoption" UCI
// command. The function updates the corresponding UCI option ("name")
// to the given value ("value").
void set_option(UCIParser& up) {
string token, name, value;
string token, name;
string value = "true"; // UCI buttons don't have a "value" field
if (!(up >> token) || token != "name") // operator>>() skips any whitespace
return;
if (!(up >> name))
return;
up >> token; // Consume "name" token
up >> name; // Read option name
// Handle names with included spaces
while (up >> token && token != "value")
name += (" " + token);
name += " " + token;
if (Options.find(name) == Options.end())
{
cout << "No such option: " << name << endl;
return;
}
// Is a button ?
if (token != "value")
{
Options[name].set_value("true");
return;
}
if (!(up >> value))
return;
up >> value; // Read option value
// Handle values with included spaces
while (up >> token)
value += (" " + token);
value += " " + token;
Options[name].set_value(value);
if (Options.find(name) != Options.end())
Options[name].set_value(value);
else
cout << "No such option: " << name << endl;
}
// go() is called when Stockfish receives the "go" UCI command. The
// input parameter is a UCIParser. It is assumed that this
// parser has consumed the first token of the UCI command ("go"),
// and is ready to read the second token. The function sets the
// thinking time and other parameters from the input string, and
// calls think() (defined in search.cpp) with the appropriate
// parameters. Returns false if a quit command is received while
// thinking, returns true otherwise.
// go() is called when engine receives the "go" UCI command. The
// function sets the thinking time and other parameters from the input
// string, and then calls think(). Returns false if a quit command
// is received while thinking, true otherwise.
bool go(Position& pos, UCIParser& up) {
string token;
int time[2] = {0, 0}, inc[2] = {0, 0};
int movesToGo = 0, depth = 0, nodes = 0, moveTime = 0;
bool infinite = false, ponder = false;
Move searchMoves[MOVES_MAX];
searchMoves[0] = MOVE_NONE;
SearchLimits limits;
Move searchMoves[MAX_MOVES], *cur = searchMoves;
int time[] = { 0, 0 }, inc[] = { 0, 0 };
while (up >> token)
{
if (token == "infinite")
infinite = true;
limits.infinite = true;
else if (token == "ponder")
ponder = true;
limits.ponder = true;
else if (token == "wtime")
up >> time[0];
up >> time[WHITE];
else if (token == "btime")
up >> time[1];
up >> time[BLACK];
else if (token == "winc")
up >> inc[0];
up >> inc[WHITE];
else if (token == "binc")
up >> inc[1];
up >> inc[BLACK];
else if (token == "movestogo")
up >> movesToGo;
up >> limits.movesToGo;
else if (token == "depth")
up >> depth;
up >> limits.maxDepth;
else if (token == "nodes")
up >> nodes;
up >> limits.maxNodes;
else if (token == "movetime")
up >> moveTime;
up >> limits.maxTime;
else if (token == "searchmoves")
{
int numOfMoves = 0;
while (up >> token)
searchMoves[numOfMoves++] = move_from_uci(pos, token);
searchMoves[numOfMoves] = MOVE_NONE;
}
*cur++ = move_from_uci(pos, token);
}
*cur = MOVE_NONE;
limits.time = time[pos.side_to_move()];
limits.increment = inc[pos.side_to_move()];
assert(pos.is_ok());
return think(pos, infinite, ponder, time, inc, movesToGo,
depth, nodes, moveTime, searchMoves);
return think(pos, limits, searchMoves);
}
// perft() is called when engine receives the "perft" command.
// The function calls perft() passing the required search depth
// then prints counted leaf nodes and elapsed time.
void perft(Position& pos, UCIParser& up) {
int depth, tm, n;
int depth, time;
int64_t n;
if (!(up >> depth))
return;
tm = get_system_time();
time = get_system_time();
n = perft(pos, depth * ONE_PLY);
tm = get_system_time() - tm;
time = get_system_time() - time;
std::cout << "\nNodes " << n
<< "\nTime (ms) " << tm
<< "\nNodes/second " << int(n / (tm / 1000.0)) << std::endl;
<< "\nTime (ms) " << time
<< "\nNodes/second " << int(n / (time / 1000.0)) << std::endl;
}
}
+56 -64
View File
@@ -60,56 +60,45 @@ static string stringify(const T& v) {
}
/// init_uci_options() initializes the UCI options to their hard coded default
/// OptionsMap c'tor initializes the UCI options to their hard coded default
/// values and initializes the default value of "Threads" and "Minimum Split Depth"
/// parameters according to the number of CPU cores.
void init_uci_options() {
OptionsMap::OptionsMap() {
Options["Use Search Log"] = Option(false);
Options["Search Log Filename"] = Option("SearchLog.txt");
Options["Book File"] = Option("book.bin");
Options["Best Book Move"] = Option(false);
Options["Mobility (Middle Game)"] = Option(100, 0, 200);
Options["Mobility (Endgame)"] = Option(100, 0, 200);
Options["Pawn Structure (Middle Game)"] = Option(100, 0, 200);
Options["Pawn Structure (Endgame)"] = Option(100, 0, 200);
Options["Passed Pawns (Middle Game)"] = Option(100, 0, 200);
Options["Passed Pawns (Endgame)"] = Option(100, 0, 200);
Options["Space"] = Option(100, 0, 200);
Options["Aggressiveness"] = Option(100, 0, 200);
Options["Cowardice"] = Option(100, 0, 200);
Options["Check Extension (PV nodes)"] = Option(2, 0, 2);
Options["Check Extension (non-PV nodes)"] = Option(1, 0, 2);
Options["Single Evasion Extension (PV nodes)"] = Option(2, 0, 2);
Options["Single Evasion Extension (non-PV nodes)"] = Option(2, 0, 2);
Options["Mate Threat Extension (PV nodes)"] = Option(2, 0, 2);
Options["Mate Threat Extension (non-PV nodes)"] = Option(2, 0, 2);
Options["Pawn Push to 7th Extension (PV nodes)"] = Option(1, 0, 2);
Options["Pawn Push to 7th Extension (non-PV nodes)"] = Option(1, 0, 2);
Options["Passed Pawn Extension (PV nodes)"] = Option(1, 0, 2);
Options["Passed Pawn Extension (non-PV nodes)"] = Option(0, 0, 2);
Options["Pawn Endgame Extension (PV nodes)"] = Option(2, 0, 2);
Options["Pawn Endgame Extension (non-PV nodes)"] = Option(2, 0, 2);
Options["Minimum Split Depth"] = Option(4, 4, 7);
Options["Maximum Number of Threads per Split Point"] = Option(5, 4, 8);
Options["Threads"] = Option(1, 1, MAX_THREADS);
Options["Use Sleeping Threads"] = Option(false);
Options["Hash"] = Option(32, 4, 8192);
Options["Clear Hash"] = Option(false, "button");
Options["Ponder"] = Option(true);
Options["OwnBook"] = Option(true);
Options["MultiPV"] = Option(1, 1, 500);
Options["Emergency Move Horizon"] = Option(40, 0, 50);
Options["Emergency Base Time"] = Option(200, 0, 60000);
Options["Emergency Move Time"] = Option(70, 0, 5000);
Options["Minimum Thinking Time"] = Option(20, 0, 5000);
Options["UCI_Chess960"] = Option(false); // Just a dummy but needed by GUIs
Options["UCI_AnalyseMode"] = Option(false);
OptionsMap& o = *this;
o["Use Search Log"] = UCIOption(false);
o["Search Log Filename"] = UCIOption("SearchLog.txt");
o["Book File"] = UCIOption("book.bin");
o["Best Book Move"] = UCIOption(false);
o["Mobility (Middle Game)"] = UCIOption(100, 0, 200);
o["Mobility (Endgame)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200);
o["Space"] = UCIOption(100, 0, 200);
o["Aggressiveness"] = UCIOption(100, 0, 200);
o["Cowardice"] = UCIOption(100, 0, 200);
o["Minimum Split Depth"] = UCIOption(4, 4, 7);
o["Maximum Number of Threads per Split Point"] = UCIOption(5, 4, 8);
o["Threads"] = UCIOption(1, 1, MAX_THREADS);
o["Use Sleeping Threads"] = UCIOption(false);
o["Hash"] = UCIOption(32, 4, 8192);
o["Clear Hash"] = UCIOption(false, "button");
o["Ponder"] = UCIOption(true);
o["OwnBook"] = UCIOption(true);
o["MultiPV"] = UCIOption(1, 1, 500);
o["Skill Level"] = UCIOption(20, 0, 20);
o["Emergency Move Horizon"] = UCIOption(40, 0, 50);
o["Emergency Base Time"] = UCIOption(200, 0, 30000);
o["Emergency Move Time"] = UCIOption(70, 0, 5000);
o["Minimum Thinking Time"] = UCIOption(20, 0, 5000);
o["UCI_Chess960"] = UCIOption(false);
o["UCI_AnalyseMode"] = UCIOption(false);
// Set some SMP parameters accordingly to the detected CPU count
Option& thr = Options["Threads"];
Option& msd = Options["Minimum Split Depth"];
UCIOption& thr = o["Threads"];
UCIOption& msd = o["Minimum Split Depth"];
thr.defaultValue = thr.currentValue = stringify(cpu_count());
@@ -118,40 +107,41 @@ void init_uci_options() {
}
/// print_uci_options() prints all the UCI options to the standard output,
/// in chronological insertion order (the idx field) and in the format
/// defined by the UCI protocol.
/// OptionsMap::print_all() returns a string with all the UCI options in chronological
/// insertion order (the idx field) and in the format defined by the UCI protocol.
void print_uci_options() {
string OptionsMap::print_all() const {
for (size_t i = 0; i <= Options.size(); i++)
for (OptionsMap::const_iterator it = Options.begin(); it != Options.end(); ++it)
std::stringstream s;
for (size_t i = 0; i <= size(); i++)
for (OptionsMap::const_iterator it = begin(); it != end(); ++it)
if (it->second.idx == i)
{
const Option& o = it->second;
cout << "\noption name " << it->first << " type " << o.type;
const UCIOption& o = it->second;
s << "\noption name " << it->first << " type " << o.type;
if (o.type != "button")
cout << " default " << o.defaultValue;
s << " default " << o.defaultValue;
if (o.type == "spin")
cout << " min " << o.minValue << " max " << o.maxValue;
s << " min " << o.minValue << " max " << o.maxValue;
break;
}
cout << endl;
return s.str();
}
/// Option class c'tors
Option::Option(const char* def) : type("string"), idx(Options.size()), minValue(0), maxValue(0)
UCIOption::UCIOption(const char* def) : type("string"), minValue(0), maxValue(0), idx(Options.size())
{ defaultValue = currentValue = def; }
Option::Option(bool def, string t) : type(t), idx(Options.size()), minValue(0), maxValue(0)
UCIOption::UCIOption(bool def, string t) : type(t), minValue(0), maxValue(0), idx(Options.size())
{ defaultValue = currentValue = (def ? "true" : "false"); }
Option::Option(int def, int minv, int maxv) : type("spin"), idx(Options.size()), minValue(minv), maxValue(maxv)
UCIOption::UCIOption(int def, int minv, int maxv) : type("spin"), minValue(minv), maxValue(maxv), idx(Options.size())
{ defaultValue = currentValue = stringify(def); }
@@ -159,20 +149,22 @@ Option::Option(int def, int minv, int maxv) : type("spin"), idx(Options.size()),
/// the GUI to check for option's limits, but we could receive the new value
/// directly from the user by teminal window. So let's check the bounds anyway.
void Option::set_value(const string& value) {
void UCIOption::set_value(const string& v) {
assert(!type.empty());
if ( (type == "check" || type == "button")
&& !(value == "true" || value == "false"))
if (v.empty())
return;
if ((type == "check" || type == "button") != (v == "true" || v == "false"))
return;
if (type == "spin")
{
int v = atoi(value.c_str());
if (v < minValue || v > maxValue)
int val = atoi(v.c_str());
if (val < minValue || val > maxValue)
return;
}
currentValue = value;
currentValue = v;
}
+29 -24
View File
@@ -25,56 +25,61 @@
#include <map>
#include <string>
class Option {
class UCIOption {
public:
Option() {} // To allow insertion in a std::map
Option(const char* defaultValue);
Option(bool defaultValue, std::string type = "check");
Option(int defaultValue, int minValue, int maxValue);
UCIOption() {} // To be used in a std::map
UCIOption(const char* defaultValue);
UCIOption(bool defaultValue, std::string type = "check");
UCIOption(int defaultValue, int minValue, int maxValue);
void set_value(const std::string& value);
void set_value(const std::string& v);
template<typename T> T value() const;
private:
friend void init_uci_options();
friend void print_uci_options();
friend class OptionsMap;
std::string defaultValue, currentValue, type;
size_t idx;
int minValue, maxValue;
size_t idx;
};
/// Custom comparator because UCI options should not be case sensitive
struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const;
};
/// Our options container is actually a map with a customized c'tor
class OptionsMap : public std::map<std::string, UCIOption, CaseInsensitiveLess> {
public:
OptionsMap();
std::string print_all() const;
};
extern OptionsMap Options;
/// Option::value() definition and specializations
template<typename T>
inline T Option::value() const {
T UCIOption::value() const {
assert(type == "spin");
return T(atoi(currentValue.c_str()));
}
template<>
inline std::string Option::value<std::string>() const {
inline std::string UCIOption::value<std::string>() const {
assert(type == "string");
return currentValue;
}
template<>
inline bool Option::value<bool>() const {
inline bool UCIOption::value<bool>() const {
assert(type == "check" || type == "button");
return currentValue == "true";
}
// Custom comparator because UCI options should not be case sensitive
struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const;
};
typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
extern OptionsMap Options;
extern void init_uci_options();
extern void print_uci_options();
#endif // !defined(UCIOPTION_H_INCLUDED)
-111
View File
@@ -1,111 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(VALUE_H_INCLUDED)
#define VALUE_H_INCLUDED
////
//// Types
////
enum ValueType {
VALUE_TYPE_NONE = 0,
VALUE_TYPE_UPPER = 1, // Upper bound
VALUE_TYPE_LOWER = 2, // Lower bound
VALUE_TYPE_EXACT = VALUE_TYPE_UPPER | VALUE_TYPE_LOWER
};
enum Value {
VALUE_ZERO = 0,
VALUE_DRAW = 0,
VALUE_KNOWN_WIN = 15000,
VALUE_MATE = 30000,
VALUE_INFINITE = 30001,
VALUE_NONE = 30002,
VALUE_ENSURE_SIGNED = -1
};
ENABLE_OPERATORS_ON(Value);
enum ScaleFactor {
SCALE_FACTOR_ZERO = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
/// Score enum keeps a midgame and an endgame value in a single
/// integer (enum), first LSB 16 bits are used to store endgame
/// value, while upper bits are used for midgame value.
// Compiler is free to choose the enum type as long as can keep
// its data, so ensure Score to be an integer type.
enum Score {
SCORE_ZERO = 0,
SCORE_ENSURE_32_BITS_SIZE_P = (1 << 16),
SCORE_ENSURE_32_BITS_SIZE_N = -(1 << 16)
};
ENABLE_OPERATORS_ON(Score);
// Extracting the _signed_ lower and upper 16 bits it not so trivial
// because according to the standard a simple cast to short is
// implementation defined and so is a right shift of a signed integer.
inline Value mg_value(Score s) { return Value(((int(s) + 32768) & ~0xffff) / 0x10000); }
// Unfortunatly on Intel 64 bit we have a small speed regression, so use a faster code in
// this case, although not 100% standard compliant it seems to work for Intel and MSVC.
#if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER))
inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); }
#else
inline Value eg_value(Score s) { return Value((int)(unsigned(s) & 0x7fffu) - (int)(unsigned(s) & 0x8000u)); }
#endif
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
// Division must be handled separately for each term
inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); }
// Only declared but not defined. We don't want to multiply two scores due to
// a very high risk of overflow. So user should explicitly convert to integer.
inline Score operator*(Score s1, Score s2);
////
//// Inline functions
////
inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
inline Value operator- (Value v, int i) { return Value(int(v) - i); }
inline Value value_mate_in(int ply) {
return VALUE_MATE - ply;
}
inline Value value_mated_in(int ply) {
return -VALUE_MATE + ply;
}
#endif // !defined(VALUE_H_INCLUDED)