Compare commits

...

97 Commits

Author SHA1 Message Date
Marco Costalba 11763d2b7f Stockfish 1.2
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-29 12:18:49 +01:00
Marco Costalba d99a95df29 Micro optimization in extension()
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-29 12:18:36 +01:00
Marco Costalba 0da1d6a846 Remove a gcc warning on an unused variable
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-29 12:18:23 +01:00
Marco Costalba 5d94305af3 Properly handle odd depths in razor formula
A little bit more aggressive, but should be more
in line with the depths logic.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-28 19:38:44 +01:00
Marco Costalba e4fd9a2df7 Safer razoring formula
Add also the possibility to razor at ply one.
It is disable dby default but it seems stronger
against Stockfish itself. It is still not clear if
is stronger against other engines. By now leave
disabled.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-28 12:55:33 +01:00
Marco Costalba aedc6c6f1f Don't silently accept an option name mismatch
With this we could have found earlier the futility
name option bug!

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-28 12:37:13 +01:00
Marco Costalba dae4e7df07 Tweak again futility margings
Lower margins near the leafs, higher at high depth.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-25 20:08:45 +01:00
Marco Costalba 96a32eec69 Expose new futility margin interface to UCI
Now futility margin it's actually a scale factor
to apply to the base ones.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-25 19:41:24 +01:00
Marco Costalba e46d3670fd Tweak futility margins
Less prune at the bottom and at the middle, a bit more
at the top.

After 747 games: +215 =345 -187 +13 elo

Also introduced a vector of margins, now that start to be a lot
it is a more flexible solution.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-25 12:05:45 +01:00
Marco Costalba 8cd5cb930d Try razoring only for depth > OnePly
Because razoring verification after qsearch() cuts more
then 40% of candidates, do not waste a costly qsearch for
nodes at depth one that will be probably discarded anyway
by futility.

Also tight razoring conditions to keep dangerous false
negatives below 0,05%. Still not clear if it is enough.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-24 09:35:57 +01:00
Marco Costalba 2feb9d5100 Futility pruning till ply 6 included
Seems good:

After 796 games: +211 = 393 -192 +8 elo

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-24 09:34:09 +01:00
Marco Costalba d11426c777 Fix a comment
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-23 12:02:10 +01:00
Marco Costalba e96f56adfa Merge futility pruning from Glaurung 2.2
It seems much more powerful then previous one.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-23 12:01:48 +01:00
Marco Costalba e3b03f13b3 Passed pawns: consider enemy rooks or queens from behind
Merged from Glaurung 2.2

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-22 12:10:15 +01:00
Marco Costalba 54b7da120f King safety: retire rook contact check
Merged from Glaurung 2.2

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-22 12:07:00 +01:00
Marco Costalba 3fafc9768a Set the 'Problem' variable only at ply == 1
Bug fix merged from Glaurung 2.2 for search_pv()

Added the same fix also to sp_search_pv() where
was missing.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-22 11:44:00 +01:00
Marco Costalba 23490bd825 Retire EvaluatePawnStorms and UseEasyMove constants
Merged from Glaurung 2.2

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 20:10:20 +01:00
Marco Costalba 61c6a3d5a0 Merge cosmetics from Glaurung 2.2
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 17:02:34 +01:00
Marco Costalba 31d4f0b734 Merge space weigth evaluation fromGlaurung 2.2
Is a new evaluation rule that gives bonus in midgame
to the side that has more space behind pawns for its
minor pieces.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 16:26:36 +01:00
Marco Costalba f178f0a291 Merged two new endgames from Glaurung 2.2
It is two bishop against a knight and two minor
pieces against one minor piece.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 15:38:10 +01:00
Marco Costalba 72ca727b38 SEE: add support for enpassant moves
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 10:44:41 +01:00
Marco Costalba 2d0146fe1d Call poll() before to check for stopped search
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 10:44:12 +01:00
Marco Costalba 96d0501735 Less aggressive razoring
Use a margin to compare with beta so that positions
that after the verifying qsearch have gained a lot of points
are not discarded just becasue not above beta.

Also remove the second condition on depth <= OnePly, it
was too risky and added only a 2% more of pruned nodes.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 10:42:39 +01:00
Marco Costalba b58ad355ca Revert: "Do not razor when in check"
It is slightly weaker after 500 games. Keep the
check on mate values.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-18 22:01:56 +01:00
Marco Costalba 17000d1ea0 Trigger of PawnEndgameExtension if capture is not a pawn
Instead of a rook.

This gives an unexpected graeat increase!

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-17 19:36:51 +01:00
Marco Costalba b09cbaebb9 search_pv: an enpassant move is a capture
Fix the logic in search_pv and sp_search_pv

An additional issue to consider is that a castle move
is not a capture but destination square is not empty.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-16 22:20:53 +01:00
Marco Costalba 725c504a5f qsearch: take in account enpassant in futility formula
Should not change anything at ELO level but it is
the correct thing to do.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-16 22:20:42 +01:00
Marco Costalba bfbfc24d07 qsearch: do not call evaluate when in check
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-16 22:20:18 +01:00
Marco Costalba a55b06d3c9 Restore development versioning and LSN filtering
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-15 22:23:03 +01:00
Marco Costalba ecc19381b4 Do not razor while in check or value is mate
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-15 22:20:03 +01:00
Marco Costalba dae2f600d6 Do not null search when beta is a mate value
Also do not return unproven mates in null search.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-15 22:19:25 +01:00
Marco Costalba 4c294932e7 Better document null move dynamic reduction
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-15 22:18:56 +01:00
Marco Costalba c831b00544 Introduce beta counters to order moves at ply one
Instead of number of searched nodes use the number of
opponent beta-cutoff occurred under the move subtree.

After 570 games 1+0 we have: +150 =288 -132 (+11 ELO)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-15 22:13:42 +01:00
Marco Costalba 5b853c9be6 Debugging: move debug function definitions in misc.cpp
Also activate writing on log file.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-14 17:26:05 +01:00
Marco Costalba 5f8f83bc05 Debugging: print to file
Print debug info on log file, not only on std::cout

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-14 14:57:17 +01:00
Marco Costalba 8ee3124487 Fix two bugs in ok_to_prune() (2)
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-12 22:27:43 +01:00
Marco Costalba 07b45151d2 Disable "Null driven IID" by default
Testing is not clear. Probably we need to test
at deeper depths to have some clear results.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-11 20:39:08 +01:00
Marco Costalba 9e3ab9099f Null move only for depth > OnePly
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-11 20:38:52 +01:00
Marco Costalba f09884d72f Null driven IID: remove IIDMargin from see() condition
This seems to cut searched nodes also more.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-10 21:43:09 +01:00
Marco Costalba ab29d8df67 Fix inflate pawns.cpp fallout
Catched counting the nodes searched at
fixed depth. A quick and reliable cross check,
expecially in inflate only patches.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-10 21:35:07 +01:00
Marco Costalba 389dc0e83b Add behind_bb() helper to simplify code logic
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-10 00:14:24 +01:00
Marco Costalba 67aac4889e Space inflate pawns.cpp
Hopefully no functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-10 00:14:15 +01:00
Marco Costalba aaad48464b Add a see() function that take only destination square
In this case firstlocates the least valuable attacker, if any,
then proceed as usual.

This will be used by next patch.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-10 00:13:59 +01:00
Marco Costalba 235df6a887 Stockfish 1.1a
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-08 12:03:46 +01:00
Marco Costalba 8d86c87e1e Add "Null driven IID" UCI option (default true)
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-08 10:57:40 +01:00
Marco Costalba c172af1b61 Null move driven internal iterative deepening
When a null move fails low due to a capture, try
to detect if without the capture we are above beta,
in this case there is a good possibility this is
a cut-node and the capture is just a null move
artifact due to side to move change. So if we still
don't have a TT move it's a good time to start an IID.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-08 10:46:52 +01:00
Marco Costalba b7c36d078b Stockfish 1.1
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 17:18:59 +01:00
Marco Costalba db1b0bfa1d Revert see() shortcut for LxH and equal captures
It happens that more then 70% of cases are HxL, where
we call see() anyway. The mesured saving of calling
see is about 0,5% of total time, but considering the
added burden in score_captures() the saving is only
0,35% locally and due to more difficult inlining of
the function it ends up that we have no advantage at all,
possibly a small slow down!

So revert.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 12:32:23 +01:00
Marco Costalba 6f946f823c Fix two gcc warnings in san.cpp
One good, the other silly.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 12:32:11 +01:00
Marco Costalba 2de78ebc7b Fix an Intel warning in san.cpp
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 12:31:48 +01:00
Marco Costalba 32934c0c8d MovePicker: avoid calling see() for LxH and equal captures
No functional change but should speed-up the captures scoring.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 11:37:19 +01:00
Marco Costalba b4fcfed55b Null capture pruning
Null move can fail low because of a capture artifact due
to the side to move change. Try to detect this condition
and fail high instead.

This pruning is very powerful, around 7% of nodes, but is
still experimental so is disabled by default.

Set UseNullCapturePruning to true to enable.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 11:31:14 +01:00
Marco Costalba dc2302b701 Position::move_is_capture() does not handle MOVE_NONE
Actually square 0 can be dirty, so that move_is_capture(0)
can return any random values.

Add an assert to be sure it is caught.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 11:18:31 +01:00
Marco Costalba 268c12bf31 Allow to call Position::print() from MovePicker
Fix a recursion issue that gives a stack overflow.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 11:18:08 +01:00
Marco Costalba 9f943a132a san.cpp: rewrite broken move_from_san
Use a state machine to parse the input. Fixed
the many broken cases.

Tested on more then 15 milions nodes.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-05 09:06:51 +01:00
Marco Costalba 20390fcb3c san.cpp cleanup
Hopefully no functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-05 09:06:30 +01:00
Marco Costalba d95b9189d8 Revert opponent time advantage logic
Strength increase was due to an hidden bug introduced
by the patch, namely the time per move to /30 instead
of /40 (see previous patch).

After testing this feature do not add any substantial
increase so is removed.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-30 19:23:33 +01:00
Marco Costalba 9d5d69e435 Revert sigmoid interpolator
After deep test (1000 games) it seems do not improve anything,
actually seems slightly weaker.

So remove it for now.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-30 21:16:47 +01:00
Marco Costalba 08265aef81 san.cpp pass position as constant reference
Make a copy of the position when needed instead
of passing as a reference. It is cleaner and
let us to simplify also Position::print()

A small space inflate while there.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-30 01:25:16 +01:00
Marco Costalba 9d1e4d041d piece_type_to_char() default argument in declaration
Default argument should be in declaration where it
is visible through header include, not in definition.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-30 00:38:33 +01:00
Marco Costalba cff3a6d33e Revert threat move ordering
Does not seem to improve anything.

Anyhow idea is nice, maybe we still have to find
correct recipe.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-26 11:22:30 +01:00
Marco Costalba 20a2ca366f Tweak allocated time per move
It seems better to give more time in middle game then
at the end.

Also Toga uses the same limit.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-25 20:41:06 +01:00
Marco Costalba c3ba5fb9d3 Rewrok the extendeable patch
Cleanup and document.

The real functional change is that not mate threat
moves are never pruned, as could happen before.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-25 20:40:49 +01:00
Marco Costalba eba8925d81 MovePicker: take advantage of threat move for ordering
If the null move was refuted by a capture then give a
bonus if we move away the captured piece.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-25 20:40:34 +01:00
Marco Costalba ee9f650242 Use extendable instead of depth extension
We can have depth(0) also in problematic cases
according to how extensions are tweaked by the user.

In any case we don't want to prune these moves.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-25 20:40:12 +01:00
Marco Costalba 8a0dd93c56 Generate moves for powerful pieces first
This seems to reduce searched nodes by a
surprising 2.5%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-24 20:43:29 +01:00
Marco Costalba 5a72ff128e Benchmark: print nodes searched at the end of testing
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-24 20:43:11 +01:00
Marco Costalba 3f63dd1023 Easy debug macro enabling
Now you don't need to toggle show_debug_xxxx anymore

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-22 15:59:26 +01:00
Marco Costalba 62ab7e4612 Introduce node limited benchmarking
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-22 11:56:14 +01:00
Marco Costalba 1867785231 Introduce depth limited benchmarking
Also print some more info.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-22 11:02:05 +01:00
Marco Costalba bac4da70c9 Remove an include in movepick.h
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Signed-off-by: unknown <Marco@.(none)>
2008-11-19 22:15:41 +01:00
Marco Costalba 8189ae9e1c Fix a silly bug that disabled second killer
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Signed-off-by: unknown <Marco@.(none)>
2008-11-19 22:15:26 +01:00
Marco Costalba 1d525bb45f qsearch: restore pruning of pv nodes with negative SEE
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Signed-off-by: unknown <Marco@.(none)>
2008-11-19 22:15:14 +01:00
Marco Costalba da7d872eda Fix Intel warnings and init_search_stack argument
Should be a reference not a copy!

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:46:12 +01:00
Marco Costalba 49d52b8266 Set killer slots number to 2
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:48 +01:00
Marco Costalba 75d001addd MovePicker: fix a nasty bug in EvalInfo optimization
EvalInfo has missing attack info when a specialized
endgame function is used.

We missed this case and were using an empty attack bitboard
instead so that no captures were generated for endgames.

After testing the EvalInfo optimization gave worst results,
so after a (long) debug session this nasty bug was found.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:48 +01:00
Marco Costalba 7daaf03b39 Add and use update_killers()
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:48 +01:00
Marco Costalba 2e778445d5 Add and use move_is_killer() helper
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:48 +01:00
Marco Costalba 93bc05cf69 Convert killers to a vector
Add infrastructure to threat killer moves as a vector,
this will allow us to easily parametrize the number of
killer moves, instead of hardcode this value to two as is now.

This patch just add the infrastructure, no functional
change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:47 +01:00
Marco Costalba a7227ac26f qsearch: do not prune pv nodes with negative SEE
Also small micro-optimization, take a line out of
the moves loop.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:47 +01:00
Marco Costalba bb0da595a7 Disable per-square MVV/LVA for now
Needs more testing.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:47 +01:00
Marco Costalba 20d7197a9b MovePicker: use EvalInfo to skip generating captures
When we know already no captures are possible in a given
position.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:46 +01:00
Marco Costalba f4758ced90 Position::to_fen(): fix a bug in side to move representation
Was introduced almost two months ago in patch:
"Space inflate Position::to_fen()"

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:46 +01:00
Marco Costalba 3c05bd70eb Print the move in addition to position
Teach Position::print() to optionally print a
given move in san notation in addition to
the ASCII representation of the board.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:46 +01:00
Marco Costalba 7000e100bd Enable per-square MVV/LVA
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:46 +01:00
Marco Costalba 2ed22e4fc8 MovePicker:find bad captures during scoring
Instead of pospone until picking. No functional
change and probably no performance change but it is
needed for following patch.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:46 +01:00
Marco Costalba 940c53c366 MovePicker: introduce per square MVV/LVA ordering
Just added the infrastructure, no functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:45 +01:00
Marco Costalba 4df8651c82 Fix hashfull info
Do not count has a replacement when a TT entry is
written in an empty slot.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:45 +01:00
Marco Costalba 2d4e2bc62a Fix in ok_to_history(): castle move is not a capture
It is erroneusly considered a capture because king
moves on the same square of the rook.

Use the correct function Position::move_is_capture()
instead of the open coded (and buggy) one.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:40 +01:00
Marco Costalba d89a03cc35 Small tidyup of TranspositionTable::store()
Hopefully without bugs this time!

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:40 +01:00
Marco Costalba 34b1d0538b Fix a logic bug in TranspositionTable::store()
Make the logic work as advertised in the function
description.

Still a fallback from TT cleanup.

This should be less serious then the one in retrieve(),
but it's still a bug.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:40 +01:00
Marco Costalba cdf1f23bc5 Micro optimization of update_history()
Remove an useless comparison.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:40 +01:00
Marco Costalba e86468cc63 Use cut-off checks in qsearch as killer moves
Killers should not be captures, but checks are not
and are produced also in qsearch.

Use this information, will be useful for move ordering.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:40 +01:00
Marco Costalba e7bad2687a Smaller null move reduction when depth is high
Lower probability to miss something important.

It seems to increase strenght. Idea form Cyclone.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:39 +01:00
Marco Costalba 3f610e2b13 Introduce LastIterations variable
Is set during the last iteration.

Sometime also during the second last.

During the last iteration is set in the 95% of cases.

During the second last is set in the 40% of cases.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:39 +01:00
Marco Costalba 6cc11272e2 Restore development versioning and LSN filtering
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:39 +01:00
Marco Costalba a28c17ecb6 Fix a missed initialization in get_option_value()
Spotted and reported by Dann Corbit.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-04 20:59:11 +01:00
26 changed files with 1389 additions and 813 deletions
+24 -5
View File
@@ -71,8 +71,8 @@ void benchmark(const std::string& commandLine) {
std::istringstream csVal(commandLine); std::istringstream csVal(commandLine);
std::istringstream csStr(commandLine); std::istringstream csStr(commandLine);
std::string ttSize, threads, fileName; std::string ttSize, threads, fileName, limitType;
int val, secsPerPos; int val, secsPerPos, maxDepth, maxNodes;
csStr >> ttSize; csStr >> ttSize;
csVal >> val; csVal >> val;
@@ -95,8 +95,18 @@ void benchmark(const std::string& commandLine) {
set_option_value("Use Search Log", "true"); set_option_value("Use Search Log", "true");
set_option_value("Search Log Filename", "bench.txt"); set_option_value("Search Log Filename", "bench.txt");
csVal >> secsPerPos; csVal >> val;
csVal >> fileName; csVal >> fileName;
csVal >> limitType;
secsPerPos = maxDepth = maxNodes = 0;
if (limitType == "time")
secsPerPos = val * 1000;
else if (limitType == "depth")
maxDepth = val;
else
maxNodes = val;
std::vector<std::string> positions; std::vector<std::string> positions;
@@ -121,12 +131,21 @@ void benchmark(const std::string& commandLine) {
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
positions.push_back(std::string(BenchmarkPositions[i])); positions.push_back(std::string(BenchmarkPositions[i]));
int startTime = get_system_time();
std::vector<std::string>::iterator it; std::vector<std::string>::iterator it;
for (it = positions.begin(); it != positions.end(); ++it) int cnt = 1;
int64_t totalNodes = 0;
for (it = positions.begin(); it != positions.end(); ++it, ++cnt)
{ {
Move moves[1] = {MOVE_NONE}; Move moves[1] = {MOVE_NONE};
int dummy[2] = {0, 0}; int dummy[2] = {0, 0};
Position pos(*it); Position pos(*it);
think(pos, true, false, 0, dummy, dummy, 0, 0, 0, secsPerPos * 1000, moves); std::cout << "\nProcessing position " << cnt << '/' << positions.size() << std::endl << std::endl;
think(pos, true, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves);
totalNodes += nodes_searched();
} }
std::cout << "\nProcessing time (ms) " << get_system_time() - startTime << std::endl
<< "Nodes searched " << totalNodes << std::endl
<< "Press any key to exit" << std::endl;
std::cin >> fileName;
} }
+4 -2
View File
@@ -250,8 +250,8 @@ Bitboard BMask[64];
int BAttackIndex[64]; int BAttackIndex[64];
Bitboard BAttacks[0x1480]; Bitboard BAttacks[0x1480];
Bitboard SetMaskBB[64]; Bitboard SetMaskBB[65];
Bitboard ClearMaskBB[64]; Bitboard ClearMaskBB[65];
Bitboard StepAttackBB[16][64]; Bitboard StepAttackBB[16][64];
Bitboard RayBB[64][8]; Bitboard RayBB[64][8];
@@ -433,6 +433,8 @@ namespace {
// be necessary to touch any of them. // be necessary to touch any of them.
void init_masks() { void init_masks() {
SetMaskBB[SQ_NONE] = 0ULL;
ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE];
for(Square s = SQ_A1; s <= SQ_H8; s++) { for(Square s = SQ_A1; s <= SQ_H8; s++) {
SetMaskBB[s] = (1ULL << s); SetMaskBB[s] = (1ULL << s);
ClearMaskBB[s] = ~SetMaskBB[s]; ClearMaskBB[s] = ~SetMaskBB[s];
+15 -2
View File
@@ -113,8 +113,8 @@ extern const Bitboard RankBB[8];
extern const Bitboard RelativeRankBB[2][8]; extern const Bitboard RelativeRankBB[2][8];
extern const Bitboard InFrontBB[2][8]; extern const Bitboard InFrontBB[2][8];
extern Bitboard SetMaskBB[64]; extern Bitboard SetMaskBB[65];
extern Bitboard ClearMaskBB[64]; extern Bitboard ClearMaskBB[65];
extern Bitboard StepAttackBB[16][64]; extern Bitboard StepAttackBB[16][64];
extern Bitboard RayBB[64][8]; extern Bitboard RayBB[64][8];
@@ -236,6 +236,19 @@ 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 in_front_bb(opposite_color(c), square_rank(s));
}
/// ray_bb() gives a bitboard representing all squares along the ray in a /// ray_bb() gives a bitboard representing all squares along the ray in a
/// given direction from a given square. /// given direction from a given square.
+49
View File
@@ -62,6 +62,13 @@ KRKNEvaluationFunction EvaluateKNKR = KRKNEvaluationFunction(BLACK);
KQKREvaluationFunction EvaluateKQKR = KQKREvaluationFunction(WHITE); KQKREvaluationFunction EvaluateKQKR = KQKREvaluationFunction(WHITE);
KQKREvaluationFunction EvaluateKRKQ = KQKREvaluationFunction(BLACK); KQKREvaluationFunction EvaluateKRKQ = KQKREvaluationFunction(BLACK);
// KBB vs KN:
KBBKNEvaluationFunction EvaluateKBBKN = KBBKNEvaluationFunction(WHITE);
KBBKNEvaluationFunction EvaluateKNKBB = KBBKNEvaluationFunction(BLACK);
// K and two minors vs K and one or two minors:
KmmKmEvaluationFunction EvaluateKmmKm = KmmKmEvaluationFunction(WHITE);
/// Scaling functions /// Scaling functions
@@ -187,6 +194,8 @@ KRKPEvaluationFunction::KRKPEvaluationFunction(Color c) : EndgameEvaluationFunct
KRKBEvaluationFunction::KRKBEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { } KRKBEvaluationFunction::KRKBEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KRKNEvaluationFunction::KRKNEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { } KRKNEvaluationFunction::KRKNEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KQKREvaluationFunction::KQKREvaluationFunction(Color c) : EndgameEvaluationFunction(c) { } KQKREvaluationFunction::KQKREvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KBBKNEvaluationFunction::KBBKNEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KmmKmEvaluationFunction::KmmKmEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
ScalingFunction::ScalingFunction(Color c) { ScalingFunction::ScalingFunction(Color c) {
@@ -420,6 +429,36 @@ Value KQKREvaluationFunction::apply(const Position &pos) {
} }
Value KBBKNEvaluationFunction::apply(const Position &pos) {
assert(pos.piece_count(strongerSide, BISHOP) == 2);
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.pawns() == EmptyBoardBB);
Value result = BishopValueEndgame;
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
Square nsq = pos.piece_list(weakerSide, KNIGHT, 0);
// Bonus for attacking king close to defending king
result += distance_bonus(square_distance(wksq, bksq));
// Bonus for driving the defending king and knight apart
result += Value(square_distance(bksq, nsq) * 32);
// Bonus for restricting the knight's mobility
result += Value((8 - count_1s_max_15(pos.piece_attacks<KNIGHT>(nsq))) * 8);
return (strongerSide == pos.side_to_move())? result : -result;
}
Value KmmKmEvaluationFunction::apply(const Position &pos) {
return Value(0);
}
/// KBPKScalingFunction scales endgames where the stronger side has king, /// KBPKScalingFunction scales endgames where the stronger side has king,
/// bishop and one or more pawns. It checks for draws with rook pawns and a /// bishop and one or more pawns. It checks for draws with rook pawns and a
/// bishop of the wrong color. If such a draw is detected, ScaleFactor(0) is /// bishop of the wrong color. If such a draw is detected, ScaleFactor(0) is
@@ -604,6 +643,16 @@ ScaleFactor KRPKRScalingFunction::apply(const Position &pos) {
- (8 * square_distance(wpsq, queeningSq) + - (8 * square_distance(wpsq, queeningSq) +
2 * square_distance(wksq, queeningSq))); 2 * square_distance(wksq, queeningSq)));
// If the pawn is not far advanced, and the defending king is somewhere in
// the pawn's path, it's probably a draw:
if(r <= RANK_4 && bksq > wpsq) {
if(square_file(bksq) == square_file(wpsq))
return ScaleFactor(10);
if(abs(square_file(bksq) - square_file(wpsq)) == 1
&& square_distance(wksq, bksq) > 2)
return ScaleFactor(24 - 2 * square_distance(wksq, bksq));
}
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
+20
View File
@@ -99,6 +99,20 @@ public:
Value apply(const Position &pos); Value apply(const Position &pos);
}; };
// KBB vs KN:
class KBBKNEvaluationFunction : public EndgameEvaluationFunction {
public:
KBBKNEvaluationFunction(Color C);
Value apply(const Position &pos);
};
// K and two minors vs K and one or two minors:
class KmmKmEvaluationFunction : public EndgameEvaluationFunction {
public:
KmmKmEvaluationFunction(Color c);
Value apply(const Position &pos);
};
/// Abstract base class for all evaluation scaling functions: /// Abstract base class for all evaluation scaling functions:
@@ -205,6 +219,12 @@ extern KRKNEvaluationFunction EvaluateKRKN, EvaluateKNKR;
// KQ vs KR: // KQ vs KR:
extern KQKREvaluationFunction EvaluateKQKR, EvaluateKRKQ; extern KQKREvaluationFunction EvaluateKQKR, EvaluateKRKQ;
// KBB vs KN:
extern KBBKNEvaluationFunction EvaluateKBBKN, EvaluateKNKBB;
// K and two minors vs K and one or two minors:
extern KmmKmEvaluationFunction EvaluateKmmKm;
// KBP vs K: // KBP vs K:
extern KBPKScalingFunction ScaleKBPK, ScaleKKBP; extern KBPKScalingFunction ScaleKBPK, ScaleKKBP;
+85 -40
View File
@@ -52,6 +52,7 @@ namespace {
int WeightPassedPawnsMidgame = 0x100; int WeightPassedPawnsMidgame = 0x100;
int WeightPassedPawnsEndgame = 0x100; int WeightPassedPawnsEndgame = 0x100;
int WeightKingSafety[2] = { 0x100, 0x100 }; int WeightKingSafety[2] = { 0x100, 0x100 };
int WeightSpace;
// Internal evaluation weights. These are applied on top of the evaluation // Internal evaluation weights. These are applied on top of the evaluation
// weights read from UCI parameters. The purpose is to be able to change // weights read from UCI parameters. The purpose is to be able to change
@@ -63,8 +64,9 @@ namespace {
const int WeightPawnStructureEndgameInternal = 0x100; const int WeightPawnStructureEndgameInternal = 0x100;
const int WeightPassedPawnsMidgameInternal = 0x100; const int WeightPassedPawnsMidgameInternal = 0x100;
const int WeightPassedPawnsEndgameInternal = 0x100; const int WeightPassedPawnsEndgameInternal = 0x100;
const int WeightKingSafetyInternal = 0x100; const int WeightKingSafetyInternal = 0x110;
const int WeightKingOppSafetyInternal = 0x100; const int WeightKingOppSafetyInternal = 0x110;
const int WeightSpaceInternal = 0x30;
// Visually better to define tables constants // Visually better to define tables constants
typedef Value V; typedef Value V;
@@ -203,6 +205,19 @@ namespace {
((1ULL << SQ_A8) | (1ULL << SQ_H8)) ((1ULL << SQ_A8) | (1ULL << SQ_H8))
}; };
// The SpaceMask[color] contains area of the board which is consdered 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
// friendly minor pieces.
const Bitboard SpaceMask[2] = {
(1ULL<<SQ_C2) | (1ULL<<SQ_D2) | (1ULL<<SQ_E2) | (1ULL<<SQ_F2) |
(1ULL<<SQ_C3) | (1ULL<<SQ_D3) | (1ULL<<SQ_E3) | (1ULL<<SQ_F3) |
(1ULL<<SQ_C4) | (1ULL<<SQ_D4) | (1ULL<<SQ_E4) | (1ULL<<SQ_F4),
(1ULL<<SQ_C7) | (1ULL<<SQ_D7) | (1ULL<<SQ_E7) | (1ULL<<SQ_F7) |
(1ULL<<SQ_C6) | (1ULL<<SQ_D6) | (1ULL<<SQ_E6) | (1ULL<<SQ_F6) |
(1ULL<<SQ_C5) | (1ULL<<SQ_D5) | (1ULL<<SQ_E5) | (1ULL<<SQ_F5)
};
/// King safety constants and variables. The king safety scores are taken /// King safety constants and variables. The king safety scores are taken
/// from the array SafetyTable[]. Various little "meta-bonuses" measuring /// from the array SafetyTable[]. Various little "meta-bonuses" measuring
/// the strength of the attack are added up into an integer, which is used /// the strength of the attack are added up into an integer, which is used
@@ -215,8 +230,7 @@ namespace {
const int KnightAttackWeight = 2; const int KnightAttackWeight = 2;
// Bonuses for safe checks for each piece type. // Bonuses for safe checks for each piece type.
int QueenContactCheckBonus = 4; int QueenContactCheckBonus = 3;
int RookContactCheckBonus = 2;
int QueenCheckBonus = 2; int QueenCheckBonus = 2;
int RookCheckBonus = 1; int RookCheckBonus = 1;
int BishopCheckBonus = 1; int BishopCheckBonus = 1;
@@ -246,18 +260,18 @@ namespace {
// in init_safety(). // in init_safety().
Value SafetyTable[100]; Value SafetyTable[100];
// Pawn and material hash tables, indexed by the current thread id: // Pawn and material hash tables, indexed by the current thread id
PawnInfoTable *PawnTable[8] = {0, 0, 0, 0, 0, 0, 0, 0}; PawnInfoTable *PawnTable[8] = {0, 0, 0, 0, 0, 0, 0, 0};
MaterialInfoTable *MaterialTable[8] = {0, 0, 0, 0, 0, 0, 0, 0}; MaterialInfoTable *MaterialTable[8] = {0, 0, 0, 0, 0, 0, 0, 0};
// Sizes of pawn and material hash tables: // Sizes of pawn and material hash tables
const int PawnTableSize = 16384; const int PawnTableSize = 16384;
const int MaterialTableSize = 1024; const int MaterialTableSize = 1024;
// Array which gives the number of nonzero bits in an 8-bit integer: // Array which gives the number of nonzero bits in an 8-bit integer:
uint8_t BitCount8Bit[256]; uint8_t BitCount8Bit[256];
// Function prototypes: // Function prototypes
void evaluate_knight(const Position &p, Square s, Color us, EvalInfo &ei); void evaluate_knight(const Position &p, Square s, Color us, EvalInfo &ei);
void evaluate_bishop(const Position &p, Square s, Color us, EvalInfo &ei); void evaluate_bishop(const Position &p, Square s, Color us, EvalInfo &ei);
void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei); void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei);
@@ -270,6 +284,7 @@ namespace {
void evaluate_trapped_bishop_a1h1(const Position &pos, Square s, Color us, void evaluate_trapped_bishop_a1h1(const Position &pos, Square s, Color us,
EvalInfo &ei); EvalInfo &ei);
void evaluate_space(const Position &p, Color us, EvalInfo &ei);
inline Value apply_weight(Value v, int w); inline Value apply_weight(Value v, int w);
Value scale_by_game_phase(Value mv, Value ev, Phase ph, const ScaleFactor sf[]); Value scale_by_game_phase(Value mv, Value ev, Phase ph, const ScaleFactor sf[]);
@@ -407,6 +422,13 @@ Value evaluate(const Position &pos, EvalInfo &ei, int threadID) {
ei.mgValue += ei.pi->kingside_storm_value(WHITE) ei.mgValue += ei.pi->kingside_storm_value(WHITE)
- ei.pi->queenside_storm_value(BLACK); - ei.pi->queenside_storm_value(BLACK);
// Evaluate space for both sides
if (ei.mi->space_weight() > 0)
{
evaluate_space(pos, WHITE, ei);
evaluate_space(pos, BLACK, ei);
}
} }
// Mobility // Mobility
@@ -414,7 +436,7 @@ Value evaluate(const Position &pos, EvalInfo &ei, int threadID) {
ei.egValue += apply_weight(ei.egMobility, WeightMobilityEndgame); ei.egValue += apply_weight(ei.egMobility, WeightMobilityEndgame);
// If we don't already have an unusual scale factor, check for opposite // If we don't already have an unusual scale factor, check for opposite
// colored bishop endgames, and use a lower scale for those: // colored bishop endgames, and use a lower scale for those
if ( phase < PHASE_MIDGAME if ( phase < PHASE_MIDGAME
&& pos.opposite_colored_bishops() && pos.opposite_colored_bishops()
&& ( (factor[WHITE] == SCALE_FACTOR_NORMAL && ei.egValue > Value(0)) && ( (factor[WHITE] == SCALE_FACTOR_NORMAL && ei.egValue > Value(0))
@@ -527,6 +549,7 @@ void read_weights(Color us) {
WeightKingSafety[us] = weight_option("Cowardice", WeightKingSafetyInternal); WeightKingSafety[us] = weight_option("Cowardice", WeightKingSafetyInternal);
WeightKingSafety[them] = weight_option("Aggressiveness", WeightKingOppSafetyInternal); WeightKingSafety[them] = weight_option("Aggressiveness", WeightKingOppSafetyInternal);
WeightSpace = weight_option("Space", WeightSpaceInternal);
init_safety(); init_safety();
} }
@@ -613,7 +636,6 @@ namespace {
void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei) { void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei) {
//Bitboard b = p.rook_attacks(s);
Bitboard b = rook_attacks_bb(s, p.occupied_squares() & ~p.rooks_and_queens(us)); Bitboard b = rook_attacks_bb(s, p.occupied_squares() & ~p.rooks_and_queens(us));
ei.attackedBy[us][ROOK] |= b; ei.attackedBy[us][ROOK] |= b;
@@ -744,14 +766,14 @@ namespace {
Bitboard occ = p.occupied_squares(), b, b2; Bitboard occ = p.occupied_squares(), b, b2;
// Initialize the 'attackUnits' variable, which is used later on as an // Initialize the 'attackUnits' variable, which is used later on as an
// index to the SafetyTable[] array. The initial is based on the number // index to the SafetyTable[] array. The initial value is based on the
// and types of the attacking pieces, the number of attacked and // number and types of the attacking pieces, the number of attacked and
// undefended squares around the king, the square of the king, and the // undefended squares around the king, the square of the king, and the
// quality of the pawn shelter. // quality of the pawn shelter.
int attackUnits = int attackUnits =
Min((ei.kingAttackersCount[them] * ei.kingAttackersWeight[them]) / 2, 25) Min((ei.kingAttackersCount[them] * ei.kingAttackersWeight[them]) / 2, 25)
+ (ei.kingAdjacentZoneAttacksCount[them] + count_1s_max_15(undefended)) * 3 + (ei.kingAdjacentZoneAttacksCount[them] + count_1s_max_15(undefended)) * 3
+ InitKingDanger[relative_square(us, s)] - shelter / 32; + InitKingDanger[relative_square(us, s)] - (shelter >> 5);
// Analyse safe queen contact checks // Analyse safe queen contact checks
b = undefended & ei.attacked_by(them, QUEEN) & ~p.pieces_of_color(them); b = undefended & ei.attacked_by(them, QUEEN) & ~p.pieces_of_color(them);
@@ -797,25 +819,8 @@ namespace {
} }
} }
} }
// Analyse safe rook contact checks:
if (RookContactCheckBonus)
{
b = undefended & ei.attacked_by(them, ROOK) & ~p.pieces_of_color(them);
if (b)
{
Bitboard attackedByOthers =
ei.attacked_by(them, PAWN) | ei.attacked_by(them, KNIGHT)
| ei.attacked_by(them, BISHOP) | ei.attacked_by(them, QUEEN);
b &= attackedByOthers; // Analyse safe distance checks
if (b)
{
int count = count_1s_max_15(b);
attackUnits += (RookContactCheckBonus * count * (sente? 2 : 1));
}
}
}
// Analyse safe distance checks:
if (QueenCheckBonus > 0 || RookCheckBonus > 0) if (QueenCheckBonus > 0 || RookCheckBonus > 0)
{ {
b = p.piece_attacks<ROOK>(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us); b = p.piece_attacks<ROOK>(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
@@ -931,6 +936,13 @@ namespace {
b2 = squares_in_front_of(us, s); b2 = squares_in_front_of(us, s);
b3 = b2 & ei.attacked_by(them); b3 = b2 & ei.attacked_by(them);
b4 = b2 & ei.attacked_by(us); b4 = b2 & ei.attacked_by(us);
// If there is an enemy rook or queen attacking the pawn from behind,
// add all X-ray attacks by the rook or queen.
if(bit_is_set(ei.attacked_by(them,ROOK)|ei.attacked_by(them,QUEEN),s)
&& squares_behind(us, s) & pos.rooks_and_queens(them))
b3 = b2;
if((b2 & pos.pieces_of_color(them)) == EmptyBoardBB) { if((b2 & pos.pieces_of_color(them)) == EmptyBoardBB) {
// There are no enemy pieces in the pawn's path! Are any of the // There are no enemy pieces in the pawn's path! Are any of the
// squares in the pawn's path attacked by the enemy? // squares in the pawn's path attacked by the enemy?
@@ -1101,14 +1113,54 @@ namespace {
} }
// apply_weight applies an evaluation weight to a value. // evaluate_space() computes the space evaluation for a given side. The
// space evaluation is a simple bonus based on the number of safe squares
// available for minor pieces on the central four files on ranks 2--4. Safe
// squares one, two or three squares behind a friendly pawn are counted
// twice. Finally, the space bonus is scaled by a weight taken from the
// material hash table.
void evaluate_space(const Position &pos, Color us, EvalInfo &ei) {
Color them = opposite_color(us);
// Find the safe squares for our pieces inside the area defined by
// SpaceMask[us]. A square is unsafe it is attacked by an enemy
// pawn, or if it is undefended and attacked by an enemy piece.
Bitboard safeSquares =
SpaceMask[us] & ~pos.pawns(us) & ~ei.attacked_by(them, PAWN)
& ~(~ei.attacked_by(us) & ei.attacked_by(them));
// Find all squares which are at most three squares behind some friendly
// pawn.
Bitboard behindFriendlyPawns = pos.pawns(us);
if(us == WHITE) {
behindFriendlyPawns |= (behindFriendlyPawns >> 8);
behindFriendlyPawns |= (behindFriendlyPawns >> 16);
}
else {
behindFriendlyPawns |= (behindFriendlyPawns << 8);
behindFriendlyPawns |= (behindFriendlyPawns << 16);
}
int space =
count_1s_max_15(safeSquares)
+ count_1s_max_15(behindFriendlyPawns & safeSquares);
ei.mgValue += Sign[us] *
apply_weight(Value(space * ei.mi->space_weight()), WeightSpace);
}
// apply_weight() applies an evaluation weight to a value
inline Value apply_weight(Value v, int w) { inline Value apply_weight(Value v, int w) {
return (v*w) / 0x100; return (v*w) / 0x100;
} }
// scale_by_game_phase interpolates between a middle game and an endgame // scale_by_game_phase() interpolates between a middle game and an endgame
// score, based on game phase. It also scales the return value by a // score, based on game phase. It also scales the return value by a
// ScaleFactor array. // ScaleFactor array.
@@ -1120,13 +1172,7 @@ namespace {
ev = apply_scale_factor(ev, sf[(ev > Value(0) ? WHITE : BLACK)]); ev = apply_scale_factor(ev, sf[(ev > Value(0) ? WHITE : BLACK)]);
// Linearized sigmoid interpolator Value result = Value(int((mv * ph + ev * (128 - ph)) / 128));
int sph = int(ph);
sph -= (64 - sph) / 4;
sph = Min(PHASE_MIDGAME, Max(PHASE_ENDGAME, sph));
Value result = Value(int((mv * sph + ev * (128 - sph)) / 128));
return Value(int(result) & ~(GrainSize - 1)); return Value(int(result) & ~(GrainSize - 1));
} }
@@ -1162,7 +1208,6 @@ namespace {
void init_safety() { void init_safety() {
QueenContactCheckBonus = get_option_value_int("Queen Contact Check Bonus"); QueenContactCheckBonus = get_option_value_int("Queen Contact Check Bonus");
RookContactCheckBonus = get_option_value_int("Rook Contact Check Bonus");
QueenCheckBonus = get_option_value_int("Queen Check Bonus"); QueenCheckBonus = get_option_value_int("Queen Check Bonus");
RookCheckBonus = get_option_value_int("Rook Check Bonus"); RookCheckBonus = get_option_value_int("Rook Check Bonus");
BishopCheckBonus = get_option_value_int("Bishop Check Bonus"); BishopCheckBonus = get_option_value_int("Bishop Check Bonus");
+5 -3
View File
@@ -71,16 +71,18 @@ int main(int argc, char *argv[]) {
// Process command line arguments // Process command line arguments
if (argc >= 2 && string(argv[1]) == "bench") if (argc >= 2 && string(argv[1]) == "bench")
{ {
if (argc < 4 || argc > 6) if (argc < 4 || argc > 7)
{ {
std::cout << "Usage: glaurung bench <hash size> <threads> " std::cout << "Usage: glaurung bench <hash size> <threads> "
<< "[time = 60s] [fen positions file = default]" << "[time = 60s] [fen positions file = default] "
<< "[time, depth or node limited = time]"
<< std::endl; << std::endl;
exit(0); exit(0);
} }
string time = argc > 4 ? argv[4] : "60"; string time = argc > 4 ? argv[4] : "60";
string fen = argc > 5 ? argv[5] : "default"; string fen = argc > 5 ? argv[5] : "default";
benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen); string lim = argc > 6 ? argv[6] : "time";
benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen + " " + lim);
return 0; return 0;
} }
+30
View File
@@ -161,6 +161,22 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
mi->evaluationFunction = &EvaluateKKX; mi->evaluationFunction = &EvaluateKKX;
return mi; return mi;
} }
else if ( pos.pawns() == EmptyBoardBB
&& pos.rooks() == EmptyBoardBB
&& pos.queens() == EmptyBoardBB)
{
// Minor piece endgame with at least one minor piece per side,
// and no pawns.
assert(pos.knights(WHITE) | pos.bishops(WHITE));
assert(pos.knights(BLACK) | pos.bishops(BLACK));
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
{
mi->evaluationFunction = &EvaluateKmmKm;
return mi;
}
}
// OK, we didn't find any special evaluation function for the current // OK, we didn't find any special evaluation function for the current
// material configuration. Is there a suitable scaling function? // material configuration. Is there a suitable scaling function?
@@ -221,6 +237,18 @@ 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)
{
int minorPieceCount = pos.piece_count(WHITE, KNIGHT)
+ pos.piece_count(BLACK, KNIGHT)
+ pos.piece_count(WHITE, BISHOP)
+ pos.piece_count(BLACK, BISHOP);
mi->spaceWeight = minorPieceCount * minorPieceCount;
}
// Evaluate the material balance // Evaluate the material balance
int sign; int sign;
@@ -309,6 +337,8 @@ EndgameFunctions::EndgameFunctions() {
add(z[W][KNIGHT][1] ^ z[B][ROOK][1], &EvaluateKNKR); add(z[W][KNIGHT][1] ^ z[B][ROOK][1], &EvaluateKNKR);
add(z[W][QUEEN][1] ^ z[B][ROOK][1], &EvaluateKQKR); add(z[W][QUEEN][1] ^ z[B][ROOK][1], &EvaluateKQKR);
add(z[W][ROOK][1] ^ z[B][QUEEN][1], &EvaluateKRKQ); add(z[W][ROOK][1] ^ z[B][QUEEN][1], &EvaluateKRKQ);
add(z[W][BISHOP][2] ^ z[B][KNIGHT][1], &EvaluateKBBKN);
add(z[W][KNIGHT][1] ^ z[B][BISHOP][2], &EvaluateKNKBB);
add(z[W][KNIGHT][1] ^ z[W][PAWN][1], W, &ScaleKNPK); add(z[W][KNIGHT][1] ^ z[W][PAWN][1], W, &ScaleKNPK);
add(z[B][KNIGHT][1] ^ z[B][PAWN][1], B, &ScaleKKNP); add(z[B][KNIGHT][1] ^ z[B][PAWN][1], B, &ScaleKKNP);
+12
View File
@@ -52,6 +52,7 @@ public:
Value mg_value() const; Value mg_value() const;
Value eg_value() const; Value eg_value() const;
ScaleFactor scale_factor(const Position& pos, Color c) const; ScaleFactor scale_factor(const Position& pos, Color c) const;
int space_weight() const;
bool specialized_eval_exists() const; bool specialized_eval_exists() const;
Value evaluate(const Position& pos) const; Value evaluate(const Position& pos) const;
@@ -64,6 +65,7 @@ private:
uint8_t factor[2]; uint8_t factor[2];
EndgameEvaluationFunction* evaluationFunction; EndgameEvaluationFunction* evaluationFunction;
ScalingFunction* scalingFunction[2]; ScalingFunction* scalingFunction[2];
uint8_t spaceWeight;
}; };
@@ -120,6 +122,7 @@ inline void MaterialInfo::clear() {
mgValue = egValue = 0; mgValue = egValue = 0;
factor[WHITE] = factor[BLACK] = uint8_t(SCALE_FACTOR_NORMAL); factor[WHITE] = factor[BLACK] = uint8_t(SCALE_FACTOR_NORMAL);
spaceWeight = 0;
evaluationFunction = NULL; evaluationFunction = NULL;
scalingFunction[WHITE] = scalingFunction[BLACK] = NULL; scalingFunction[WHITE] = scalingFunction[BLACK] = NULL;
} }
@@ -144,6 +147,15 @@ inline ScaleFactor MaterialInfo::scale_factor(const Position& pos, Color c) cons
} }
/// MaterialInfo::space_weight() simply returns the weight for the space
/// evaluation for this material configuration.
inline int MaterialInfo::space_weight() const {
return spaceWeight;
}
/// MaterialInfo::specialized_eval_exists decides whether there is a /// MaterialInfo::specialized_eval_exists decides whether there is a
/// specialized evaluation function for the current material configuration, /// specialized evaluation function for the current material configuration,
/// or if the normal evaluation function should be used. /// or if the normal evaluation function should be used.
+56 -1
View File
@@ -37,6 +37,7 @@ int gettimeofday(struct timeval * tp, struct timezone * tzp);
#endif #endif
#include <cassert>
#include <cstdio> #include <cstdio>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
@@ -49,6 +50,9 @@ int gettimeofday(struct timeval * tp, struct timezone * tzp);
//// Variables //// Variables
//// ////
static const std::string AppName = "Stockfish";
static const std::string AppTag = "";
long dbg_cnt0 = 0; long dbg_cnt0 = 0;
long dbg_cnt1 = 0; long dbg_cnt1 = 0;
@@ -60,6 +64,42 @@ bool dbg_show_hit_rate = false;
//// Functions //// 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_cnt0++;
dbg_cnt1 += v;
}
void dbg_print_hit_rate() { void dbg_print_hit_rate() {
std::cout << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1 std::cout << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
@@ -73,6 +113,19 @@ void dbg_print_mean() {
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << std::endl; << (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << std::endl;
} }
void dbg_print_hit_rate(std::ofstream& logFile) {
logFile << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
<< " hit rate (%) " << (dbg_cnt1*100)/(dbg_cnt0 ? dbg_cnt0 : 1)
<< std::endl;
}
void dbg_print_mean(std::ofstream& logFile) {
logFile << "Total " << dbg_cnt0 << " Mean "
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << std::endl;
}
/// engine_name() returns the full name of the current Stockfish version. /// engine_name() returns the full name of the current Stockfish version.
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when the /// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when the
/// program was compiled) or "Stockfish <version number>", depending on whether /// program was compiled) or "Stockfish <version number>", depending on whether
@@ -90,7 +143,9 @@ const std::string engine_name() {
std::stringstream s; std::stringstream s;
std::string day = (date[4] == ' ' ? date.substr(5, 1) : date.substr(4, 2)); std::string day = (date[4] == ' ' ? date.substr(5, 1) : date.substr(4, 2));
s << "Stockfish " << date.substr(date.length() - 2) << std::setfill('0') std::string name = AppName + " " + AppTag + " ";
s << name << date.substr(date.length() - 2) << std::setfill('0')
<< std::setw(2) << mon << std::setw(2) << day; << std::setw(2) << mon << std::setw(2) << day;
return s.str(); return s.str();
+15 -14
View File
@@ -26,6 +26,7 @@
//// Includes //// Includes
//// ////
#include <fstream>
#include <string> #include <string>
@@ -37,7 +38,7 @@
/// Version number. If this is left empty, the current date (in the format /// Version number. If this is left empty, the current date (in the format
/// YYMMDD) is used as a version number. /// YYMMDD) is used as a version number.
const std::string EngineVersion = "1.01"; const std::string EngineVersion = "1.2";
//// ////
@@ -60,21 +61,21 @@ extern int Bioskey();
//// ////
//// Debug //// Debug
//// ////
extern long dbg_cnt0;
extern long dbg_cnt1;
inline void dbg_hit_on(bool b) { dbg_cnt0++; if (b) dbg_cnt1++; }
inline void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
inline void dbg_before() { dbg_cnt0++; }
inline void dbg_after() { dbg_cnt1++; }
inline void dbg_mean_of(int v) { dbg_cnt0++; dbg_cnt1 += v; }
extern void dbg_print_hit_rate();
extern void dbg_print_mean();
extern bool dbg_show_mean; extern bool dbg_show_mean;
extern bool dbg_show_hit_rate; extern bool dbg_show_hit_rate;
extern long dbg_cnt0;
extern long dbg_cnt1;
extern void dbg_hit_on(bool b);
extern void dbg_hit_on_c(bool c, bool b);
extern void dbg_before();
extern void dbg_after();
extern void dbg_mean_of(int v);
extern void dbg_print_hit_rate();
extern void dbg_print_mean();
extern void dbg_print_hit_rate(std::ofstream& logFile);
extern void dbg_print_mean(std::ofstream& logFile);
#endif // !defined(MISC_H_INCLUDED) #endif // !defined(MISC_H_INCLUDED)
+5 -4
View File
@@ -122,15 +122,16 @@ int generate_captures(const Position& pos, MoveStack* mlist) {
Bitboard target = pos.pieces_of_color(opposite_color(us)); Bitboard target = pos.pieces_of_color(opposite_color(us));
MoveStack* mlist_start = mlist; MoveStack* mlist_start = mlist;
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);
if (us == WHITE) if (us == WHITE)
mlist = generate_pawn_captures<WHITE>(pos, mlist); mlist = generate_pawn_captures<WHITE>(pos, mlist);
else else
mlist = generate_pawn_captures<BLACK>(pos, mlist); mlist = generate_pawn_captures<BLACK>(pos, mlist);
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_piece_moves<KING>(pos, mlist, us, target);
return int(mlist - mlist_start); return int(mlist - mlist_start);
} }
+91 -37
View File
@@ -26,6 +26,7 @@
#include <cassert> #include <cassert>
#include "history.h" #include "history.h"
#include "evaluate.h"
#include "movegen.h" #include "movegen.h"
#include "movepick.h" #include "movepick.h"
#include "search.h" #include "search.h"
@@ -44,7 +45,9 @@ namespace {
int MainSearchPhaseIndex; int MainSearchPhaseIndex;
int EvasionsPhaseIndex; int EvasionsPhaseIndex;
int QsearchWithChecksPhaseIndex; int QsearchWithChecksPhaseIndex;
int QsearchNoCapturesPhaseIndex;
int QsearchWithoutChecksPhaseIndex; int QsearchWithoutChecksPhaseIndex;
int NoMovesPhaseIndex;
} }
@@ -62,28 +65,38 @@ namespace {
/// search captures, promotions and some checks) and about how important good /// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node. /// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, bool pvnode, Move ttm, Move mk, MovePicker::MovePicker(const Position& p, bool pv, Move ttm,
Move k1, Move k2, Depth d) : pos(p) { const SearchStack& ss, Depth d, EvalInfo* ei) : pos(p) {
pvNode = pvnode; pvNode = pv;
ttMove = ttm; ttMove = ttm;
mateKiller = (mk == ttm)? MOVE_NONE : mk; mateKiller = (ss.mateKiller == ttm)? MOVE_NONE : ss.mateKiller;
killer1 = k1; killer1 = ss.killers[0];
killer2 = k2; killer2 = ss.killers[1];
depth = d; depth = d;
movesPicked = 0; movesPicked = 0;
numOfMoves = 0; numOfMoves = 0;
numOfBadCaptures = 0; numOfBadCaptures = 0;
dc = p.discovered_check_candidates(p.side_to_move());
// With EvalInfo we are able to know how many captures are possible before
// generating them. So avoid generating in case we know are zero.
Color us = pos.side_to_move();
Color them = opposite_color(us);
bool noCaptures = ei
&& (ei->attackedBy[us][0] & pos.pieces_of_color(them)) == 0
&& !ei->mi->specialized_eval_exists()
&& (pos.ep_square() == SQ_NONE)
&& !pos.has_pawn_on_7th(us);
if (p.is_check()) if (p.is_check())
phaseIndex = EvasionsPhaseIndex; phaseIndex = EvasionsPhaseIndex;
else if (depth > Depth(0)) else if (depth > Depth(0))
phaseIndex = MainSearchPhaseIndex; phaseIndex = MainSearchPhaseIndex;
else if (depth == Depth(0)) else if (depth == Depth(0))
phaseIndex = QsearchWithChecksPhaseIndex; phaseIndex = (noCaptures ? QsearchNoCapturesPhaseIndex : QsearchWithChecksPhaseIndex);
else else
phaseIndex = QsearchWithoutChecksPhaseIndex; phaseIndex = (noCaptures ? NoMovesPhaseIndex : QsearchWithoutChecksPhaseIndex);
dc = p.discovered_check_candidates(us);
pinned = p.pinned_pieces(p.side_to_move()); pinned = p.pinned_pieces(p.side_to_move());
finished = false; finished = false;
@@ -211,6 +224,8 @@ void MovePicker::score_captures() {
// where it is possible to recapture with the hanging piece). Exchanging // where it is possible to recapture with the hanging piece). Exchanging
// big pieces before capturing a hanging piece probably helps to reduce // big pieces before capturing a hanging piece probably helps to reduce
// the subtree size. // the subtree size.
// While scoring captures it moves all captures with negative SEE values
// to the badCaptures[] array.
Move m; Move m;
int seeValue; int seeValue;
@@ -225,8 +240,15 @@ void MovePicker::score_captures() {
else else
moves[i].score = int(pos.midgame_value_of_piece_on(move_to(m))) moves[i].score = int(pos.midgame_value_of_piece_on(move_to(m)))
-int(pos.type_of_piece_on(move_from(m))); -int(pos.type_of_piece_on(move_from(m)));
} else }
else
{
// Losing capture, move it to the badCaptures[] array
assert(numOfBadCaptures < 63);
moves[i].score = seeValue; moves[i].score = seeValue;
badCaptures[numOfBadCaptures++] = moves[i];
moves[i--] = moves[--numOfMoves];
}
} }
} }
@@ -248,10 +270,11 @@ void MovePicker::score_noncaptures() {
else else
hs = H.move_ordering_score(pos.piece_on(move_from(m)), m); hs = H.move_ordering_score(pos.piece_on(move_from(m)), m);
// Ensure moves in history are always sorted as first // Ensure history is always preferred to pst
if (hs > 0) if (hs > 0)
hs += 1000; hs += 1000;
// pst based scoring
moves[i].score = hs + pos.mg_pst_delta(m); moves[i].score = hs + pos.mg_pst_delta(m);
} }
} }
@@ -270,7 +293,6 @@ void MovePicker::score_evasions() {
} else } else
moves[i].score = H.move_ordering_score(pos.piece_on(move_from(m)), m); moves[i].score = H.move_ordering_score(pos.piece_on(move_from(m)), m);
} }
// FIXME try psqt also here
} }
void MovePicker::score_qcaptures() { void MovePicker::score_qcaptures() {
@@ -289,7 +311,10 @@ void MovePicker::score_qcaptures() {
/// find_best_index() loops across the moves and returns index of /// find_best_index() loops across the moves and returns index of
/// the highest scored one. /// the highest scored one. There is also a second version that
/// lowers the priority of moves that attack the same square,
/// so that if the best move that attack a square fails the next
/// move picked attacks a different square if any, not the same one.
int MovePicker::find_best_index() { int MovePicker::find_best_index() {
@@ -304,14 +329,48 @@ int MovePicker::find_best_index() {
return bestIndex; return bestIndex;
} }
int MovePicker::find_best_index(Bitboard* squares, int values[]) {
int hs;
Move m;
Square to;
int bestScore = -10000000, bestIndex = -1;
for (int i = movesPicked; i < numOfMoves; i++)
{
m = moves[i].move;
to = move_to(m);
if (!bit_is_set(*squares, to))
{
// Init at first use
set_bit(squares, to);
values[to] = 0;
}
hs = moves[i].score - values[to];
if (hs > bestScore)
{
bestIndex = i;
bestScore = hs;
}
}
if (bestIndex != -1)
{
// Raise value of the picked square, so next attack
// to the same square will get low priority.
to = move_to(moves[bestIndex].move);
values[to] += 0xB00;
}
return bestIndex;
}
/// MovePicker::pick_move_from_list() picks the move with the biggest score /// MovePicker::pick_move_from_list() picks the move with the biggest score
/// from a list of generated moves (moves[] or badCaptures[], depending on /// from a list of generated moves (moves[] or badCaptures[], depending on
/// the current move generation phase). It takes care not to return the /// the current move generation phase). It takes care not to return the
/// transposition table move if that has already been serched previously. /// transposition table move if that has already been serched previously.
/// While picking captures in the PH_GOOD_CAPTURES phase (i.e. while picking
/// non-losing captures in the main search), it moves all captures with
/// negative SEE values to the badCaptures[] array.
Move MovePicker::pick_move_from_list() { Move MovePicker::pick_move_from_list() {
@@ -325,23 +384,8 @@ Move MovePicker::pick_move_from_list() {
while (movesPicked < numOfMoves) while (movesPicked < numOfMoves)
{ {
int bestScore = -10000000; bestIndex = find_best_index();
bestIndex = -1;
for (int i = movesPicked; i < numOfMoves; i++)
{
if (moves[i].score < 0)
{
// Losing capture, move it to the badCaptures[] array
assert(numOfBadCaptures < 63);
badCaptures[numOfBadCaptures++] = moves[i];
moves[i--] = moves[--numOfMoves];
}
else if (moves[i].score > bestScore)
{
bestIndex = i;
bestScore = moves[i].score;
}
}
if (bestIndex != -1) // Found a good capture if (bestIndex != -1) // Found a good capture
{ {
move = moves[bestIndex].move; move = moves[bestIndex].move;
@@ -461,8 +505,9 @@ MovePicker::MovegenPhase MovePicker::current_move_type() const {
/// MovePicker::init_phase_table() initializes the PhaseTable[], /// MovePicker::init_phase_table() initializes the PhaseTable[],
/// MainSearchPhaseIndex, EvasionPhaseIndex, QsearchWithChecksPhaseIndex /// MainSearchPhaseIndex, EvasionPhaseIndex, QsearchWithChecksPhaseIndex
/// and QsearchWithoutChecksPhaseIndex variables. It is only called once /// QsearchNoCapturesPhaseIndex, QsearchWithoutChecksPhaseIndex and
/// during program startup, and never again while the program is running. /// NoMovesPhaseIndex variables. It is only called once during program
/// startup, and never again while the program is running.
void MovePicker::init_phase_table() { void MovePicker::init_phase_table() {
@@ -491,8 +536,17 @@ void MovePicker::init_phase_table() {
PhaseTable[i++] = PH_QCHECKS; PhaseTable[i++] = PH_QCHECKS;
PhaseTable[i++] = PH_STOP; PhaseTable[i++] = PH_STOP;
// Quiescence search with checks only and no captures
QsearchNoCapturesPhaseIndex = i - 1;
PhaseTable[i++] = PH_QCHECKS;
PhaseTable[i++] = PH_STOP;
// Quiescence search without checks // Quiescence search without checks
QsearchWithoutChecksPhaseIndex = i - 1; QsearchWithoutChecksPhaseIndex = i - 1;
PhaseTable[i++] = PH_QCAPTURES; PhaseTable[i++] = PH_QCAPTURES;
PhaseTable[i++] = PH_STOP; PhaseTable[i++] = PH_STOP;
// Do not generate any move
NoMovesPhaseIndex = i - 1;
PhaseTable[i++] = PH_STOP;
} }
+5 -1
View File
@@ -34,6 +34,9 @@
//// Types //// Types
//// ////
struct EvalInfo;
struct SearchStack;
/// MovePicker is a class which is used to pick one legal move at a time from /// 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 /// 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 /// moves we have reason to believe are good. The most important method is
@@ -60,7 +63,7 @@ public:
PH_STOP PH_STOP
}; };
MovePicker(const Position& p, bool pvnode, Move ttm, Move mk, Move k1, Move k2, Depth d); MovePicker(const Position& p, bool pvnode, Move ttm, const SearchStack& ss, Depth d, EvalInfo* ei = NULL);
Move get_next_move(); Move get_next_move();
Move get_next_move(Lock &lock); Move get_next_move(Lock &lock);
int number_of_moves() const; int number_of_moves() const;
@@ -77,6 +80,7 @@ private:
void score_qcaptures(); void score_qcaptures();
Move pick_move_from_list(); Move pick_move_from_list();
int find_best_index(); int find_best_index();
int find_best_index(Bitboard* squares, int values[]);
const Position& pos; const Position& pos;
Move ttMove, mateKiller, killer1, killer2; Move ttMove, mateKiller, killer1, killer2;
+178 -181
View File
@@ -86,40 +86,37 @@ namespace {
// Candidate passed pawn bonus by rank, middle game. // Candidate passed pawn bonus by rank, middle game.
const Value CandidateMidgameBonus[8] = { const Value CandidateMidgameBonus[8] = {
Value(0), Value(12), Value(12), Value(20), Value( 0), Value(12), Value(12), Value(20),
Value(40), Value(90), Value(0), Value(0) Value(40), Value(90), Value( 0), Value( 0)
}; };
// Candidate passed pawn bonus by rank, endgame. // Candidate passed pawn bonus by rank, endgame.
const Value CandidateEndgameBonus[8] = { const Value CandidateEndgameBonus[8] = {
Value(0), Value(24), Value(24), Value(40), Value( 0), Value(24), Value(24), Value(40),
Value(80), Value(180), Value(0), Value(0) Value(80), Value(180), Value(0), Value( 0)
}; };
// Evaluate pawn storms?
const bool EvaluatePawnStorms = true;
// Pawn storm tables for positions with opposite castling: // Pawn storm tables for positions with opposite castling:
const int QStormTable[64] = { const int QStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-22, -22, -22, -13, -4, 0, 0, 0, -22,-22,-22,-13,-4, 0, 0, 0,
-4, -9, -9, -9, -4, 0, 0, 0, -4, -9, -9, -9,-4, 0, 0, 0,
9, 18, 22, 18, 9, 0, 0, 0, 9, 18, 22, 18, 9, 0, 0, 0,
22, 31, 31, 22, 0, 0, 0, 0, 22, 31, 31, 22, 0, 0, 0, 0,
31, 40, 40, 31, 0, 0, 0, 0, 31, 40, 40, 31, 0, 0, 0, 0,
31, 40, 40, 31, 0, 0, 0, 0, 31, 40, 40, 31, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0
}; };
const int KStormTable[64] = { const int KStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, -4, -13, -22, -27, -27, 0, 0, 0,-4,-13,-22,-27,-27,
0, 0, 0, -4, -9, -13, -18, -18, 0, 0, 0,-4, -9,-13,-18,-18,
0, 0, 0, 0, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9,
0, 0, 0, 0, 9, 18, 27, 27, 0, 0, 0, 0, 9, 18, 27, 27,
0, 0, 0, 0, 9, 27, 40, 36, 0, 0, 0, 0, 9, 27, 40, 36,
0, 0, 0, 0, 0, 31, 40, 31, 0, 0, 0, 0, 0, 31, 40, 31,
0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0
}; };
// Pawn storm open file bonuses by file: // Pawn storm open file bonuses by file:
@@ -141,14 +138,16 @@ namespace {
/// Constructor /// Constructor
PawnInfoTable::PawnInfoTable(unsigned numOfEntries) { PawnInfoTable::PawnInfoTable(unsigned numOfEntries) {
size = numOfEntries; size = numOfEntries;
entries = new PawnInfo[size]; entries = new PawnInfo[size];
if(entries == NULL) { if (entries == NULL)
std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo)) {
<< " bytes for pawn hash table." << std::endl; std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo))
exit(EXIT_FAILURE); << " bytes for pawn hash table." << std::endl;
exit(EXIT_FAILURE);
} }
this->clear(); clear();
} }
@@ -173,6 +172,7 @@ void PawnInfoTable::clear() {
/// the same pawn structure occurs again. /// the same pawn structure occurs again.
PawnInfo *PawnInfoTable::get_pawn_info(const Position &pos) { PawnInfo *PawnInfoTable::get_pawn_info(const Position &pos) {
assert(pos.is_ok()); assert(pos.is_ok());
Key key = pos.get_pawn_key(); Key key = pos.get_pawn_key();
@@ -181,207 +181,204 @@ PawnInfo *PawnInfoTable::get_pawn_info(const Position &pos) {
// If pi->key matches the position's pawn hash key, it means that we // 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 the // have analysed this pawn structure before, and we can simply return the
// information we found the last time instead of recomputing it: // information we found the last time instead of recomputing it
if(pi->key == key) if (pi->key == key)
return pi; return pi;
// Clear the PawnInfo object, and set the key: // Clear the PawnInfo object, and set the key
pi->clear(); pi->clear();
pi->key = key; pi->key = key;
Value mgValue[2] = {Value(0), Value(0)}; Value mgValue[2] = {Value(0), Value(0)};
Value egValue[2] = {Value(0), Value(0)}; Value egValue[2] = {Value(0), Value(0)};
// Loop through the pawns for both colors: // Loop through the pawns for both colors
for(Color us = WHITE; us <= BLACK; us++) { for (Color us = WHITE; us <= BLACK; us++)
{
Color them = opposite_color(us); Color them = opposite_color(us);
Bitboard ourPawns = pos.pawns(us); Bitboard ourPawns = pos.pawns(us);
Bitboard theirPawns = pos.pawns(them); Bitboard theirPawns = pos.pawns(them);
Bitboard pawns = ourPawns; Bitboard pawns = ourPawns;
// Initialize pawn storm scores by giving bonuses for open files: // Initialize pawn storm scores by giving bonuses for open files
if(EvaluatePawnStorms) for (File f = FILE_A; f <= FILE_H; f++)
for(File f = FILE_A; f <= FILE_H; f++) if (pos.file_is_half_open(us, f))
if(pos.file_is_half_open(us, f)) { {
pi->ksStormValue[us] += KStormOpenFileBonus[f]; pi->ksStormValue[us] += KStormOpenFileBonus[f];
pi->qsStormValue[us] += QStormOpenFileBonus[f]; pi->qsStormValue[us] += QStormOpenFileBonus[f];
} }
// Loop through all pawns of the current color and score each pawn: // Loop through all pawns of the current color and score each pawn
while(pawns) { while (pawns)
Square s = pop_1st_bit(&pawns); {
File f = square_file(s); bool passed, doubled, isolated, backward, chain, candidate;
Rank r = square_rank(s); Square s = pop_1st_bit(&pawns);
bool passed, doubled, isolated, backward, chain, candidate; File f = square_file(s);
int bonus; Rank r = square_rank(s);
assert(pos.piece_on(s) == pawn_of_color(us)); assert(pos.piece_on(s) == pawn_of_color(us));
// The file containing the pawn is not half open: // The file containing the pawn is not half open
pi->halfOpenFiles[us] &= ~(1 << f); pi->halfOpenFiles[us] &= ~(1 << f);
// Passed, isolated or doubled pawn? // Passed, isolated or doubled pawn?
passed = pos.pawn_is_passed(us, s); passed = pos.pawn_is_passed(us, s);
isolated = pos.pawn_is_isolated(us, s); isolated = pos.pawn_is_isolated(us, s);
doubled = pos.pawn_is_doubled(us, s); doubled = pos.pawn_is_doubled(us, s);
if(EvaluatePawnStorms) { // We calculate kingside and queenside pawn storm scores
// We calculate kingside and queenside pawn storm // for both colors. These are used when evaluating middle
// scores for both colors. These are used when evaluating // game positions with opposite side castling.
// middle game positions with opposite side castling.
// //
// Each pawn is given a base score given by a piece square table // Each pawn is given a base score given by a piece square table
// (KStormTable[] or QStormTable[]). This score is increased if // (KStormTable[] or QStormTable[]). This score is increased if
// there are enemy pawns on adjacent files in front of the pawn. // there are enemy pawns on adjacent files in front of the pawn.
// This is because we want to be able to open files against the // This is because we want to be able to open files against the
// enemy king, and to avoid blocking the pawn structure (e.g. white // enemy king, and to avoid blocking the pawn structure (e.g. white
// pawns on h6, g5, black pawns on h7, g6, f7). // pawns on h6, g5, black pawns on h7, g6, f7).
// Kingside pawn storms: // Kingside and queenside pawn storms
bonus = KStormTable[relative_square(us, s)]; int KBonus = KStormTable[relative_square(us, s)];
if(bonus > 0 && outpost_mask(us, s) & theirPawns) { int QBonus = QStormTable[relative_square(us, s)];
switch(f) { bool outPostFlag = (KBonus > 0 && (outpost_mask(us, s) & theirPawns));
bool passedFlag = (QBonus > 0 && (passed_pawn_mask(us, s) & theirPawns));
case FILE_F: switch (f) {
bonus += bonus / 4;
case FILE_A:
QBonus += passedFlag * QBonus / 2;
break; break;
case FILE_G: case FILE_B:
bonus += bonus / 2 + bonus / 4; QBonus += passedFlag * (QBonus / 2 + QBonus / 4);
break; break;
case FILE_H: case FILE_C:
bonus += bonus / 2; QBonus += passedFlag * QBonus / 2;
break; break;
default: case FILE_F:
KBonus += outPostFlag * KBonus / 4;
break;
case FILE_G:
KBonus += outPostFlag * (KBonus / 2 + KBonus / 4);
break;
case FILE_H:
KBonus += outPostFlag * KBonus / 2;
break;
default:
break; break;
}
} }
pi->ksStormValue[us] += bonus; pi->ksStormValue[us] += KBonus;
pi->qsStormValue[us] += QBonus;
// Queenside pawn storms: // Member of a pawn chain (but not the backward one)? We could speed up
bonus = QStormTable[relative_square(us, s)]; // the test a little by introducing an array of masks indexed by color
if(bonus > 0 && passed_pawn_mask(us, s) & theirPawns) { // and square for doing the test, but because everything is hashed,
switch(f) { // it probably won't make any noticable difference.
chain = ourPawns
& neighboring_files_bb(f)
& (rank_bb(r) | rank_bb(r - (us == WHITE ? 1 : -1)));
case FILE_A: // Test for backward pawn
bonus += bonus / 2; //
break; // If the pawn is passed, isolated, or member of a pawn chain
// it cannot be backward. If can capture an enemy pawn or if
case FILE_B: // there are friendly pawns behind on neighboring files it cannot
bonus += bonus / 2 + bonus / 4; // be backward either.
break; if ( passed
|| isolated
case FILE_C: || chain
bonus += bonus / 2; || (pos.pawn_attacks(us, s) & theirPawns)
break; || (ourPawns & behind_bb(us, r) & neighboring_files_bb(f)))
backward = false;
default: else
break; {
} // We now know that there are no friendly pawns beside or behind this
// pawn on neighboring files. We now check whether the pawn is
// backward by looking in the forward direction on the neighboring
// files, and seeing whether we meet a friendly or an enemy pawn first.
Bitboard b;
if (us == WHITE)
{
for (b = pos.pawn_attacks(us, s); !(b & (ourPawns | theirPawns)); b <<= 8);
backward = (b | (b << 8)) & theirPawns;
}
else
{
for (b = pos.pawn_attacks(us, s); !(b & (ourPawns | theirPawns)); b >>= 8);
backward = (b | (b >> 8)) & theirPawns;
}
} }
pi->qsStormValue[us] += bonus;
}
// Member of a pawn chain? We could speed up the test a little by // Test for candidate passed pawn
// introducing an array of masks indexed by color and square for doing candidate = !passed
// the test, but because everything is hashed, it probably won't make && pos.file_is_half_open(them, f)
// any noticable difference. && ( count_1s_max_15(neighboring_files_bb(f) & (behind_bb(us, r) | rank_bb(r)) & ourPawns)
chain = (us == WHITE)? - count_1s_max_15(neighboring_files_bb(f) & in_front_bb(us, r) & theirPawns)
(ourPawns & neighboring_files_bb(f) & (rank_bb(r) | rank_bb(r-1))) : >= 0);
(ourPawns & neighboring_files_bb(f) & (rank_bb(r) | rank_bb(r+1)));
// In order to prevent doubled passed pawns from receiving a too big
// Test for backward pawn. // bonus, only the frontmost passed pawn on each file is considered as
// a true passed pawn.
// If the pawn is isolated, passed, or member of a pawn chain, it cannot if (passed && (ourPawns & squares_in_front_of(us, s)))
// be backward: {
if(passed || isolated || chain) // candidate = true;
backward = false; passed = false;
// If the pawn can capture an enemy pawn, it's not backward:
else if(pos.pawn_attacks(us, s) & theirPawns)
backward = false;
// Check for friendly pawns behind on neighboring files:
else if(ourPawns & in_front_bb(them, r) & neighboring_files_bb(f))
backward = false;
else {
// We now know that there is no friendly pawns beside or behind this
// pawn on neighboring files. We now check whether the pawn is
// backward by looking in the forward direction on the neighboring
// files, and seeing whether we meet a friendly or an enemy pawn first.
Bitboard b;
if(us == WHITE) {
for(b=pos.pawn_attacks(us, s); !(b&(ourPawns|theirPawns)); b<<=8);
backward = (b | (b << 8)) & theirPawns;
} }
else {
for(b=pos.pawn_attacks(us, s); !(b&(ourPawns|theirPawns)); b>>=8); // Score this pawn
backward = (b | (b >> 8)) & theirPawns; Value mv = Value(0), ev = Value(0);
if (isolated)
{
mv -= IsolatedPawnMidgamePenalty[f];
ev -= IsolatedPawnEndgamePenalty[f];
if (pos.file_is_half_open(them, f))
{
mv -= IsolatedPawnMidgamePenalty[f] / 2;
ev -= IsolatedPawnEndgamePenalty[f] / 2;
}
} }
} if (doubled)
{
// Test for candidate passed pawn. mv -= DoubledPawnMidgamePenalty[f];
candidate = ev -= DoubledPawnEndgamePenalty[f];
(!passed && pos.file_is_half_open(them, f) &&
count_1s_max_15(neighboring_files_bb(f)
& (in_front_bb(them, r) | rank_bb(r))
& ourPawns)
- count_1s_max_15(neighboring_files_bb(f) & in_front_bb(us, r)
& theirPawns)
>= 0);
// In order to prevent doubled passed pawns from receiving a too big
// bonus, only the frontmost passed pawn on each file is considered as
// a true passed pawn.
if(passed && (ourPawns & squares_in_front_of(us, s))) {
// candidate = true;
passed = false;
}
// Score this pawn:
Value mv = Value(0), ev = Value(0);
if(isolated) {
mv -= IsolatedPawnMidgamePenalty[f];
ev -= IsolatedPawnEndgamePenalty[f];
if(pos.file_is_half_open(them, f)) {
mv -= IsolatedPawnMidgamePenalty[f] / 2;
ev -= IsolatedPawnEndgamePenalty[f] / 2;
} }
} if (backward)
if(doubled) { {
mv -= DoubledPawnMidgamePenalty[f]; mv -= BackwardPawnMidgamePenalty[f];
ev -= DoubledPawnEndgamePenalty[f]; ev -= BackwardPawnEndgamePenalty[f];
} if (pos.file_is_half_open(them, f))
if(backward) { {
mv -= BackwardPawnMidgamePenalty[f]; mv -= BackwardPawnMidgamePenalty[f] / 2;
ev -= BackwardPawnEndgamePenalty[f]; ev -= BackwardPawnEndgamePenalty[f] / 2;
if(pos.file_is_half_open(them, f)) { }
mv -= BackwardPawnMidgamePenalty[f] / 2; }
ev -= BackwardPawnEndgamePenalty[f] / 2; if (chain)
{
mv += ChainMidgameBonus[f];
ev += ChainEndgameBonus[f];
}
if (candidate)
{
mv += CandidateMidgameBonus[relative_rank(us, s)];
ev += CandidateEndgameBonus[relative_rank(us, s)];
} }
}
if(chain) {
mv += ChainMidgameBonus[f];
ev += ChainEndgameBonus[f];
}
if(candidate) {
mv += CandidateMidgameBonus[relative_rank(us, s)];
ev += CandidateEndgameBonus[relative_rank(us, s)];
}
mgValue[us] += mv; mgValue[us] += mv;
egValue[us] += ev; egValue[us] += ev;
// If the pawn is passed, set the square of the pawn in the passedPawns // If the pawn is passed, set the square of the pawn in the passedPawns
// bitboard: // bitboard
if(passed) if (passed)
set_bit(&(pi->passedPawns), s); set_bit(&(pi->passedPawns), s);
} } // while(pawns)
} } // for(colors)
pi->mgValue = int16_t(mgValue[WHITE] - mgValue[BLACK]); pi->mgValue = int16_t(mgValue[WHITE] - mgValue[BLACK]);
pi->egValue = int16_t(egValue[WHITE] - egValue[BLACK]); pi->egValue = int16_t(egValue[WHITE] - egValue[BLACK]);
return pi; return pi;
} }
+4 -4
View File
@@ -120,10 +120,10 @@ inline bool PawnInfo::has_open_file_to_right(Color c, File f) const {
} }
inline void PawnInfo::clear() { inline void PawnInfo::clear() {
mgValue = egValue = 0;
passedPawns = EmptyBoardBB; Key k = key;
ksStormValue[WHITE] = ksStormValue[BLACK] = 0; memset(this, 0, sizeof(PawnInfo));
qsStormValue[WHITE] = qsStormValue[BLACK] = 0; key = k;
halfOpenFiles[WHITE] = halfOpenFiles[BLACK] = 0xFF; halfOpenFiles[WHITE] = halfOpenFiles[BLACK] = 0xFF;
} }
+1 -1
View File
@@ -72,7 +72,7 @@ const SquareDelta PawnPush[2] = {
static const char PieceChars[] = " pnbrqk"; static const char PieceChars[] = " pnbrqk";
char piece_type_to_char(PieceType pt, bool upcase = false) { char piece_type_to_char(PieceType pt, bool upcase) {
return upcase? toupper(PieceChars[pt]) : PieceChars[pt]; return upcase? toupper(PieceChars[pt]) : PieceChars[pt];
} }
+1 -1
View File
@@ -124,7 +124,7 @@ inline SquareDelta pawn_push(Color c) {
//// Prototypes //// Prototypes
//// ////
extern char piece_type_to_char(PieceType pt, bool upcase); extern char piece_type_to_char(PieceType pt, bool upcase = false);
extern PieceType piece_type_from_char(char c); extern PieceType piece_type_from_char(char c);
extern bool piece_is_ok(Piece pc); extern bool piece_is_ok(Piece pc);
extern bool piece_type_is_ok(PieceType pt); extern bool piece_type_is_ok(PieceType pt);
+105 -40
View File
@@ -31,6 +31,7 @@
#include "movepick.h" #include "movepick.h"
#include "position.h" #include "position.h"
#include "psqtab.h" #include "psqtab.h"
#include "san.h"
#include "ucioption.h" #include "ucioption.h"
@@ -38,6 +39,8 @@
//// Variables //// Variables
//// ////
extern SearchStack EmptySearchStack;
int Position::castleRightsMask[64]; int Position::castleRightsMask[64];
Key Position::zobrist[2][8][64]; Key Position::zobrist[2][8][64];
@@ -49,6 +52,7 @@ Key Position::zobSideToMove;
Value Position::MgPieceSquareTable[16][64]; Value Position::MgPieceSquareTable[16][64];
Value Position::EgPieceSquareTable[16][64]; Value Position::EgPieceSquareTable[16][64];
static bool RequestPending = false;
//// ////
//// Functions //// Functions
@@ -242,7 +246,7 @@ const std::string Position::to_fen() const {
fen += (rank > RANK_1 ? '/' : ' '); fen += (rank > RANK_1 ? '/' : ' ');
} }
fen += (sideToMove == WHITE ? 'w' : 'b') + ' '; fen += (sideToMove == WHITE ? "w " : "b ");
if (castleRights != NO_CASTLES) if (castleRights != NO_CASTLES)
{ {
if (can_castle_kingside(WHITE)) fen += 'K'; if (can_castle_kingside(WHITE)) fen += 'K';
@@ -263,29 +267,45 @@ const std::string Position::to_fen() const {
/// Position::print() prints an ASCII representation of the position to /// Position::print() prints an ASCII representation of the position to
/// the standard output. /// the standard output. If a move is given then also the san is print.
void Position::print() const { void Position::print(Move m) const {
char pieceStrings[][8] =
{"| ? ", "| P ", "| N ", "| B ", "| R ", "| Q ", "| K ", "| ? ",
"| ? ", "|=P=", "|=N=", "|=B=", "|=R=", "|=Q=", "|=K="
};
for(Rank rank = RANK_8; rank >= RANK_1; rank--) { static const std::string pieceLetters = " PNBRQK PNBRQK .";
std::cout << "+---+---+---+---+---+---+---+---+\n";
for(File file = FILE_A; file <= FILE_H; file++) { // Check for reentrancy, as example when called from inside
Square sq = make_square(file, rank); // MovePicker that is used also here in move_to_san()
Piece piece = piece_on(sq); if (RequestPending)
if(piece == EMPTY) return;
std::cout << ((square_color(sq) == WHITE)? "| " : "| . ");
else RequestPending = true;
std::cout << pieceStrings[piece];
} std::cout << std::endl;
std::cout << "|\n"; if (m != MOVE_NONE)
{
std::string col = (color_of_piece_on(move_from(m)) == BLACK ? ".." : "");
std::cout << "Move is: " << col << move_to_san(*this, m) << std::endl;
} }
std::cout << "+---+---+---+---+---+---+---+---+\n"; for (Rank rank = RANK_8; rank >= RANK_1; rank--)
std::cout << to_fen() << std::endl; {
std::cout << key << std::endl; std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
for (File file = FILE_A; file <= FILE_H; file++)
{
Square sq = make_square(file, rank);
Piece piece = piece_on(sq);
if (piece == EMPTY && square_color(sq) == WHITE)
piece = NO_PIECE;
char col = (color_of_piece_on(sq) == BLACK ? '=' : ' ');
std::cout << '|' << col << pieceLetters[piece] << col;
}
std::cout << '|' << std::endl;
}
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl
<< "Fen is: " << to_fen() << std::endl
<< "Key is: " << key << std::endl;
RequestPending = false;
} }
@@ -655,10 +675,12 @@ bool Position::move_is_check(Move m, Bitboard dcCandidates) const {
/// Position::move_is_capture() tests whether a move from the current /// Position::move_is_capture() tests whether a move from the current
/// position is a capture. /// position is a capture. Move must not be MOVE_NONE.
bool Position::move_is_capture(Move m) const { bool Position::move_is_capture(Move m) const {
assert(m != MOVE_NONE);
return ( !square_is_empty(move_to(m)) return ( !square_is_empty(move_to(m))
&& (color_of_piece_on(move_to(m)) == opposite_color(side_to_move())) && (color_of_piece_on(move_to(m)) == opposite_color(side_to_move()))
) )
@@ -1560,10 +1582,16 @@ void Position::undo_null_move(const UndoInfo &u) {
/// Position::see() is a static exchange evaluator: It tries to estimate the /// Position::see() is a static exchange evaluator: It tries to estimate the
/// material gain or loss resulting from a move. There are two versions of /// material gain or loss resulting from a move. There are three versions of
/// this function: One which takes a move as input, and one which takes a /// this function: One which takes a destination square as input, one takes a
/// 'from' and a 'to' square. The function does not yet understand promotions /// move, and one which takes a 'from' and a 'to' square. The function does
/// or en passant captures. /// not yet understand promotions or en passant captures.
int Position::see(Square to) const {
assert(square_is_ok(to));
return see(SQ_NONE, to);
}
int Position::see(Move m) const { int Position::see(Move m) const {
@@ -1573,18 +1601,22 @@ int Position::see(Move m) const {
int Position::see(Square from, Square to) const { int Position::see(Square from, Square to) const {
// Approximate material values, with pawn = 1 // Material values
static const int seeValues[18] = { static const int seeValues[18] = {
0, 1, 3, 3, 5, 10, 100, 0, 0, 1, 3, 3, 5, 10, 100, 0, 0, 0 0, PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10, 0,
0, PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10, 0,
0, 0
}; };
Bitboard attackers, occ, b; Bitboard attackers, occ, b;
assert(square_is_ok(from)); assert(square_is_ok(from) || from == SQ_NONE);
assert(square_is_ok(to)); assert(square_is_ok(to));
// Initialize colors // Initialize colors
Color us = color_of_piece_on(from); Color us = (from != SQ_NONE ? color_of_piece_on(from) : opposite_color(color_of_piece_on(to)));
Color them = opposite_color(us); Color them = opposite_color(us);
// Initialize pieces // Initialize pieces
@@ -1594,15 +1626,49 @@ int Position::see(Square from, Square to) const {
// Find all attackers to the destination square, with the moving piece // Find all attackers to the destination square, with the moving piece
// removed, but possibly an X-ray attacker added behind it. // removed, but possibly an X-ray attacker added behind it.
occ = occupied_squares(); occ = occupied_squares();
clear_bit(&occ, from);
attackers = (rook_attacks_bb(to, occ) & rooks_and_queens())
| (bishop_attacks_bb(to, occ) & bishops_and_queens())
| (piece_attacks<KNIGHT>(to) & knights())
| (piece_attacks<KING>(to) & kings())
| (pawn_attacks(WHITE, to) & pawns(BLACK))
| (pawn_attacks(BLACK, to) & pawns(WHITE));
// If the opponent has no attackers, we are finished // Handle enpassant moves
if (ep_square() == to && type_of_piece_on(from) == PAWN)
{
assert(capture == EMPTY);
Square capQq = (side_to_move() == WHITE)? (to - DELTA_N) : (to - DELTA_S);
capture = piece_on(capQq);
assert(type_of_piece_on(capQq) == PAWN);
// Remove the captured pawn
clear_bit(&occ, capQq);
}
while (true)
{
clear_bit(&occ, from);
attackers = (rook_attacks_bb(to, occ) & rooks_and_queens())
| (bishop_attacks_bb(to, occ) & bishops_and_queens())
| (piece_attacks<KNIGHT>(to) & knights())
| (piece_attacks<KING>(to) & kings())
| (pawn_attacks(WHITE, to) & pawns(BLACK))
| (pawn_attacks(BLACK, to) & pawns(WHITE));
if (from != SQ_NONE)
break;
// If we don't have any attacker we are finished
if ((attackers & pieces_of_color(us)) == EmptyBoardBB)
return 0;
// Locate the least valuable attacker to the destination square
// and use it to initialize from square.
PieceType pt;
for (pt = PAWN; !(attackers & pieces_of_color_and_type(us, pt)); pt++)
assert(pt < KING);
from = first_1(attackers & pieces_of_color_and_type(us, pt));
piece = piece_on(from);
}
// If the opponent has no attackers we are finished
if ((attackers & pieces_of_color(them)) == EmptyBoardBB) if ((attackers & pieces_of_color(them)) == EmptyBoardBB)
return seeValues[capture]; return seeValues[capture];
@@ -1905,8 +1971,7 @@ bool Position::is_mate() {
if (is_check()) if (is_check())
{ {
MovePicker mp = MovePicker(*this, false, MOVE_NONE, MOVE_NONE, MovePicker mp = MovePicker(*this, false, MOVE_NONE, EmptySearchStack, Depth(0));
MOVE_NONE, MOVE_NONE, Depth(0));
return mp.get_next_move() == MOVE_NONE; return mp.get_next_move() == MOVE_NONE;
} }
return false; return false;
+4 -3
View File
@@ -128,7 +128,7 @@ public:
// Text input/output // Text input/output
void from_fen(const std::string &fen); void from_fen(const std::string &fen);
const std::string to_fen() const; const std::string to_fen() const;
void print() const; void print(Move m = MOVE_NONE) const;
// Copying // Copying
void copy(const Position &pos); void copy(const Position &pos);
@@ -174,13 +174,13 @@ public:
// Number of pieces of each color and type // Number of pieces of each color and type
int piece_count(Color c, PieceType pt) const; int piece_count(Color c, PieceType pt) const;
// The en passant square: // The en passant square
Square ep_square() const; Square ep_square() const;
// Current king position for each color // Current king position for each color
Square king_square(Color c) const; Square king_square(Color c) const;
// Castling rights. // Castling rights
bool can_castle_kingside(Color c) const; bool can_castle_kingside(Color c) const;
bool can_castle_queenside(Color c) const; bool can_castle_queenside(Color c) const;
bool can_castle(Color c) const; bool can_castle(Color c) const;
@@ -253,6 +253,7 @@ public:
// Static exchange evaluation // Static exchange evaluation
int see(Square from, Square to) const; int see(Square from, Square to) const;
int see(Move m) const; int see(Move m) const;
int see(Square to) const;
// Accessing hash keys // Accessing hash keys
Key get_key() const; Key get_key() const;
+257 -245
View File
@@ -31,6 +31,7 @@
#include "movepick.h" #include "movepick.h"
#include "san.h" #include "san.h"
extern SearchStack EmptySearchStack;
//// ////
//// Local definitions //// Local definitions
@@ -50,7 +51,7 @@ namespace {
/// Functions /// Functions
Ambiguity move_ambiguity(Position &pos, Move m); Ambiguity move_ambiguity(const Position& pos, Move m);
const std::string time_string(int milliseconds); const std::string time_string(int milliseconds);
const std::string score_string(Value v); const std::string score_string(Value v);
} }
@@ -61,206 +62,222 @@ namespace {
//// ////
/// move_to_san() takes a position and a move as input, where it is assumed /// 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 /// that the move is a legal move from the position. The return value is
/// a string containing the move in short algebraic notation. /// a string containing the move in short algebraic notation.
const std::string move_to_san(Position &pos, Move m) { const std::string move_to_san(const Position& pos, Move m) {
std::string str;
assert(pos.is_ok()); assert(pos.is_ok());
assert(move_is_ok(m)); assert(move_is_ok(m));
if(m == MOVE_NONE) { std::string san = "";
str = "(none)";
return str;
}
else if(m == MOVE_NULL) {
str = "(null)";
return str;
}
else if(move_is_long_castle(m))
str = "O-O-O";
else if(move_is_short_castle(m))
str = "O-O";
else {
Square from, to;
Piece pc;
from = move_from(m); if (m == MOVE_NONE)
to = move_to(m); return "(none)";
pc = pos.piece_on(move_from(m)); else if (m == MOVE_NULL)
return "(null)";
str = ""; else if (move_is_long_castle(m))
san = "O-O-O";
if(type_of_piece(pc) == PAWN) { else if (move_is_short_castle(m))
if(pos.move_is_capture(m)) san = "O-O";
str += file_to_char(square_file(move_from(m))); else
} {
else { Piece pc = pos.piece_on(move_from(m));
str += piece_type_to_char(type_of_piece(pc), true); if (type_of_piece(pc) != PAWN)
{
Ambiguity amb = move_ambiguity(pos, m); san += piece_type_to_char(type_of_piece(pc), true);
switch(amb) { Square from = move_from(m);
switch (move_ambiguity(pos, m)) {
case AMBIGUITY_NONE: case AMBIGUITY_NONE:
break; break;
case AMBIGUITY_FILE:
case AMBIGUITY_FILE: san += file_to_char(square_file(from));
str += file_to_char(square_file(from)); break;
break; case AMBIGUITY_RANK:
san += rank_to_char(square_rank(from));
case AMBIGUITY_RANK: break;
str += rank_to_char(square_rank(from)); case AMBIGUITY_BOTH:
break; san += square_to_string(from);
break;
case AMBIGUITY_BOTH: default:
str += square_to_string(from); assert(false);
break; }
}
default: if (pos.move_is_capture(m))
assert(false); {
if (type_of_piece(pc) == PAWN)
san += file_to_char(square_file(move_from(m)));
san += "x";
}
san += square_to_string(move_to(m));
if (move_promotion(m))
{
san += '=';
san += piece_type_to_char(move_promotion(m), true);
} }
}
if(pos.move_is_capture(m))
str += "x";
str += square_to_string(move_to(m));
if(move_promotion(m)) {
str += "=";
str += piece_type_to_char(move_promotion(m), true);
}
} }
// Is the move check? We don't use pos.move_is_check(m) here, because // Is the move check? We don't use pos.move_is_check(m) here, because
// Position::move_is_check doesn't detect all checks (not castling moves, // Position::move_is_check doesn't detect all checks (not castling moves,
// promotions and en passant captures). // promotions and en passant captures).
UndoInfo u; UndoInfo u;
pos.do_move(m, u); Position p(pos);
if(pos.is_check()) p.do_move(m, u);
str += pos.is_mate()? "#" : "+"; if (p.is_check())
pos.undo_move(m, u); san += p.is_mate()? "#" : "+";
return str; return san;
} }
/// move_from_san() takes a position and a string as input, and tries to /// 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, /// 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 /// 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. /// if the move is illegal or ambiguous), MOVE_NONE is returned.
Move move_from_san(Position &pos, const std::string &movestr) { Move move_from_san(const Position& pos, const std::string& movestr) {
assert(pos.is_ok()); assert(pos.is_ok());
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE, MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, OnePly);
MOVE_NONE, OnePly);
// Castling moves // Castling moves
if(movestr == "O-O-O") { if (movestr == "O-O-O" || movestr == "O-O-O+")
Move m; {
while((m = mp.get_next_move()) != MOVE_NONE) Move m;
if(move_is_long_castle(m) && pos.pl_move_is_legal(m)) while ((m = mp.get_next_move()) != MOVE_NONE)
return m; if (move_is_long_castle(m) && pos.pl_move_is_legal(m))
return MOVE_NONE; return m;
}
else if(movestr == "O-O") {
Move m;
while((m = mp.get_next_move()) != MOVE_NONE)
if(move_is_short_castle(m) && pos.pl_move_is_legal(m))
return m;
return MOVE_NONE;
}
// Normal moves
const char *cstr = movestr.c_str();
const char *c;
char *cc;
char str[10];
int i;
// Initialize str[] by making a copy of movestr with the characters
// 'x', '=', '+' and '#' removed.
cc = str;
for(i=0, c=cstr; i<10 && *c!='\0' && *c!='\n' && *c!=' '; i++, c++)
if(!strchr("x=+#", *c)) {
*cc = strchr("nrq", *c)? toupper(*c) : *c;
cc++;
}
*cc = '\0';
size_t left = 0, right = strlen(str) - 1;
PieceType pt = NO_PIECE_TYPE, promotion;
Square to;
File fromFile = FILE_NONE;
Rank fromRank = RANK_NONE;
// Promotion?
if(strchr("BNRQ", str[right])) {
promotion = piece_type_from_char(str[right]);
right--;
}
else
promotion = NO_PIECE_TYPE;
// Find the moving piece:
if(left < right) {
if(strchr("BNRQK", str[left])) {
pt = piece_type_from_char(str[left]);
left++;
}
else
pt = PAWN;
}
// Find the to square:
if(left < right) {
if(str[right] < '1' || str[right] > '8' ||
str[right-1] < 'a' || str[right-1] > 'h')
return MOVE_NONE; return MOVE_NONE;
to = make_square(file_from_char(str[right-1]), rank_from_char(str[right]));
right -= 2;
} }
else else if (movestr == "O-O" || movestr == "O-O+")
{
Move m;
while ((m = mp.get_next_move()) != MOVE_NONE)
if (move_is_short_castle(m) && pos.pl_move_is_legal(m))
return m;
return MOVE_NONE; return MOVE_NONE;
// Find the file and/or rank of the from square:
if(left <= right) {
if(strchr("abcdefgh", str[left])) {
fromFile = file_from_char(str[left]);
left++;
}
if(strchr("12345678", str[left]))
fromRank = rank_from_char(str[left]);
} }
// Look for a matching move: // Normal moves. We use a simple FSM to parse the san string.
enum { START, TO_FILE, TO_RANK, PROMOTION_OR_CHECK, PROMOTION, CHECK, END };
static const std::string pieceLetters = "KQRBN";
PieceType pt = NO_PIECE_TYPE, promotion = NO_PIECE_TYPE;
File fromFile = FILE_NONE, toFile = FILE_NONE;
Rank fromRank = RANK_NONE, toRank = RANK_NONE;
Square to;
int state = START;
for (size_t i = 0; i < movestr.length(); i++)
{
char type, c = movestr[i];
if (pieceLetters.find(c) != std::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 a matching move
Move m, move = MOVE_NONE; Move m, move = MOVE_NONE;
to = make_square(toFile, toRank);
int matches = 0; int matches = 0;
while((m = mp.get_next_move()) != MOVE_NONE) { while ((m = mp.get_next_move()) != MOVE_NONE)
bool match = true; if ( pos.type_of_piece_on(move_from(m)) == pt
if(pos.type_of_piece_on(move_from(m)) != pt) && move_to(m) == to
match = false; && move_promotion(m) == promotion
else if(move_to(m) != to) && (fromFile == FILE_NONE || fromFile == square_file(move_from(m)))
match = false; && (fromRank == RANK_NONE || fromRank == square_rank(move_from(m))))
else if(move_promotion(m) != promotion) {
match = false; move = m;
else if(fromFile != FILE_NONE && fromFile != square_file(move_from(m))) matches++;
match = false; }
else if(fromRank != RANK_NONE && fromRank != square_rank(move_from(m))) return (matches == 1 ? move : MOVE_NONE);
match = false;
if(match) {
move = m;
matches++;
}
}
if(matches == 1)
return move;
else
return MOVE_NONE;
} }
@@ -271,34 +288,31 @@ Move move_from_san(Position &pos, const std::string &movestr) {
/// length of 80 characters. After a line break, 'startColumn' spaces are /// length of 80 characters. After a line break, 'startColumn' spaces are
/// inserted at the beginning of the new line. /// inserted at the beginning of the new line.
const std::string line_to_san(const Position &pos, Move line[], int startColumn, const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines) {
bool breakLines) {
Position p = Position(pos);
UndoInfo u; UndoInfo u;
std::stringstream s; std::stringstream s;
std::string moveStr; std::string moveStr;
size_t length, maxLength; size_t length = 0;
size_t maxLength = 80 - startColumn;
Position p(pos);
length = 0; for (int i = 0; line[i] != MOVE_NONE; i++)
maxLength = 80 - startColumn; {
moveStr = move_to_san(p, line[i]);
length += moveStr.length() + 1;
if (breakLines && length > maxLength)
{
s << '\n' << std::setw(startColumn) << ' ';
length = moveStr.length() + 1;
}
s << moveStr << ' ';
for(int i = 0; line[i] != MOVE_NONE; i++) { if (line[i] == MOVE_NULL)
moveStr = move_to_san(p, line[i]); p.do_null_move(u);
length += moveStr.length() + 1; else
if(breakLines && length > maxLength) { p.do_move(line[i], u);
s << "\n";
for(int j = 0; j < startColumn; j++)
s << " ";
length = moveStr.length() + 1;
}
s << moveStr << " ";
if(line[i] == MOVE_NULL)
p.do_null_move(u);
else
p.do_move(line[i], u);
} }
return s.str(); return s.str();
} }
@@ -307,26 +321,26 @@ const std::string line_to_san(const Position &pos, Move line[], int startColumn,
/// It is used to write search information to the log file (which is created /// It is used to write search information to the log file (which is created
/// when the UCI parameter "Use Search Log" is "true"). /// when the UCI parameter "Use Search Log" is "true").
const std::string pretty_pv(const Position &pos, int time, int depth, const std::string pretty_pv(const Position& pos, int time, int depth,
uint64_t nodes, Value score, Move pv[]) { uint64_t nodes, Value score, Move pv[]) {
std::stringstream s; std::stringstream s;
// Depth // Depth
s << std::setw(2) << std::setfill(' ') << depth << " "; s << std::setw(2) << depth << " ";
// Score // Score
s << std::setw(8) << score_string(score); s << std::setw(8) << score_string(score);
// Time // Time
s << std::setw(8) << std::setfill(' ') << time_string(time) << " "; s << std::setw(8) << time_string(time) << " ";
// Nodes // Nodes
if(nodes < 1000000ULL) if (nodes < 1000000ULL)
s << std::setw(8) << std::setfill(' ') << nodes << " "; s << std::setw(8) << nodes << " ";
else if(nodes < 1000000000ULL) else if (nodes < 1000000000ULL)
s << std::setw(7) << std::setfill(' ') << nodes/1000ULL << 'k' << " "; s << std::setw(7) << nodes/1000ULL << 'k' << " ";
else else
s << std::setw(7) << std::setfill(' ') << nodes/1000000ULL << 'M' << " "; s << std::setw(7) << nodes/1000000ULL << 'M' << " ";
// PV // PV
s << line_to_san(pos, pv, 30, true); s << line_to_san(pos, pv, 30, true);
@@ -337,82 +351,80 @@ const std::string pretty_pv(const Position &pos, int time, int depth,
namespace { namespace {
Ambiguity move_ambiguity(Position &pos, Move m) { Ambiguity move_ambiguity(const Position& pos, Move m) {
Square from, to;
Piece pc;
from = move_from(m); Square from = move_from(m);
to = move_to(m); Square to = move_to(m);
pc = pos.piece_on(from); Piece pc = pos.piece_on(from);
// King moves are never ambiguous, because there is never two kings of // King moves are never ambiguous, because there is never two kings of
// the same color. // the same color.
if(type_of_piece(pc) == KING) if (type_of_piece(pc) == KING)
return AMBIGUITY_NONE; return AMBIGUITY_NONE;
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE, MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, OnePly);
MOVE_NONE, OnePly);
Move mv, moveList[8]; Move mv, moveList[8];
int i, j, n;
n = 0; int n = 0;
while((mv = mp.get_next_move()) != MOVE_NONE) while ((mv = mp.get_next_move()) != MOVE_NONE)
if(move_to(mv) == to && pos.piece_on(move_from(mv)) == pc if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv))
&& pos.pl_move_is_legal(mv)) moveList[n++] = mv;
moveList[n++] = mv;
if(n == 1)
return AMBIGUITY_NONE;
j = 0; if (n == 1)
for(i = 0; i < n; i++) return AMBIGUITY_NONE;
if(square_file(move_from(moveList[i])) == square_file(from))
j++;
if(j == 1)
return AMBIGUITY_FILE;
j = 0; int f = 0, r = 0;
for(i = 0; i < n; i++) for (int i = 0; i < n; i++)
if(square_rank(move_from(moveList[i])) == square_rank(from)) {
j++; if (square_file(move_from(moveList[i])) == square_file(from))
if(j == 1) f++;
return AMBIGUITY_RANK;
if (square_rank(move_from(moveList[i])) == square_rank(from))
r++;
}
if (f == 1)
return AMBIGUITY_FILE;
if (r == 1)
return AMBIGUITY_RANK;
return AMBIGUITY_BOTH; return AMBIGUITY_BOTH;
} }
const std::string time_string(int milliseconds) { const std::string time_string(int milliseconds) {
std::stringstream s; std::stringstream s;
s << std::setfill('0');
int hours = milliseconds / (1000 * 60 * 60); int hours = milliseconds / (1000*60*60);
int minutes = (milliseconds - hours*1000*60*60) / (60*1000); int minutes = (milliseconds - hours*1000*60*60) / (1000*60);
int seconds = (milliseconds - hours*1000*60*60 - minutes*60*1000) / 1000; int seconds = (milliseconds - hours*1000*60*60 - minutes*1000*60) / 1000;
if(hours) if (hours)
s << hours << ':'; s << hours << ':';
s << std::setw(2) << std::setfill('0') << minutes << ':';
s << std::setw(2) << std::setfill('0') << seconds;
s << std::setw(2) << minutes << ':' << std::setw(2) << seconds;
return s.str(); return s.str();
} }
const std::string score_string(Value v) { const std::string score_string(Value v) {
std::stringstream s; std::stringstream s;
if(abs(v) >= VALUE_MATE - 200) { if (v >= VALUE_MATE - 200)
if(v < 0)
s << "-#" << (VALUE_MATE + v) / 2;
else
s << "#" << (VALUE_MATE - v + 1) / 2; s << "#" << (VALUE_MATE - v + 1) / 2;
} else if(v <= -VALUE_MATE + 200)
else { s << "-#" << (VALUE_MATE + v) / 2;
float floatScore = float(v) / float(PawnValueMidgame); else
if(v >= 0) {
s << '+'; float floatScore = float(v) / float(PawnValueMidgame);
s << std::setprecision(2) << std::fixed << floatScore; if (v >= 0)
s << '+';
s << std::setprecision(2) << std::fixed << floatScore;
} }
return s.str(); return s.str();
} }
} }
+4 -7
View File
@@ -36,12 +36,9 @@
//// Prototypes //// Prototypes
//// ////
extern const std::string move_to_san(Position &pos, Move m); extern const std::string move_to_san(const Position& pos, Move m);
extern Move move_from_san(Position &pos, const std::string &str); extern Move move_from_san(const Position& pos, const std::string& str);
extern const std::string line_to_san(const Position &pos, Move line[], extern const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines);
int startColumn, bool breakLines); extern const std::string pretty_pv(const Position& pos, int time, int depth, uint64_t nodes, Value score, Move pv[]);
extern const std::string pretty_pv(const Position &pos, int time, int depth,
uint64_t nodes, Value score, Move pv[]);
#endif // !defined(SAN_H_INCLUDED) #endif // !defined(SAN_H_INCLUDED)
+367 -168
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -25,7 +25,6 @@
//// Includes //// Includes
//// ////
#include "types.h"
#include "depth.h" #include "depth.h"
#include "history.h" #include "history.h"
#include "lock.h" #include "lock.h"
@@ -41,6 +40,7 @@
const int PLY_MAX = 100; const int PLY_MAX = 100;
const int PLY_MAX_PLUS_2 = 102; const int PLY_MAX_PLUS_2 = 102;
const int KILLER_MAX = 2;
//// ////
@@ -56,8 +56,9 @@ struct SearchStack {
Move pv[PLY_MAX]; Move pv[PLY_MAX];
Move currentMove; Move currentMove;
Value currentMoveCaptureValue; Value currentMoveCaptureValue;
Move mateKiller, killer1, killer2; Move mateKiller;
Move threatMove; Move threatMove;
Move killers[KILLER_MAX];
Depth reduction; Depth reduction;
}; };
@@ -66,10 +67,9 @@ struct SearchStack {
//// Global variables //// Global variables
//// ////
extern SearchStack EmptySearchStack;
extern TranspositionTable TT; extern TranspositionTable TT;
extern int ActiveThreads; extern int ActiveThreads;
extern Lock SMPLock; extern Lock SMPLock;
// Perhaps better to make H local, and pass as parameter to MovePicker? // Perhaps better to make H local, and pass as parameter to MovePicker?
+11 -15
View File
@@ -108,29 +108,25 @@ void TranspositionTable::store(const Position &pos, Value v, Depth d,
TTEntry *tte, *replace; TTEntry *tte, *replace;
tte = replace = first_entry(pos); tte = replace = first_entry(pos);
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++, tte++)
{ {
if (!(tte+i)->key()) // still empty if (!tte->key() || tte->key() == pos.get_key()) // empty or overwrite old
{
*(tte+i) = TTEntry(pos.get_key(), v, type, d, m, generation);
writes++;
return;
}
if ((tte+i)->key() == pos.get_key()) // overwrite old
{ {
if (m == MOVE_NONE) if (m == MOVE_NONE)
m = (tte+i)->move(); m = tte->move();
*(tte+i) = TTEntry(pos.get_key(), v, type, d, m, generation); *tte = TTEntry(pos.get_key(), v, type, d, m, generation);
return; return;
} }
if ( i == 0 // already is (replace == tte+i), common case else if (i == 0) // replace would be a no-op in this common case
|| replace->generation() < (tte+i)->generation())
continue; continue;
if ( replace->generation() > (tte+i)->generation() int c1 = (replace->generation() == generation ? 2 : 0);
|| (tte+i)->depth() < replace->depth()) int c2 = (tte->generation() == generation ? -2 : 0);
replace = tte+i; int c3 = (tte->depth() < replace->depth() ? 1 : 0);
if (c1 + c2 + c3 > 0)
replace = tte;
} }
*replace = TTEntry(pos.get_key(), v, type, d, m, generation); *replace = TTEntry(pos.get_key(), v, type, d, m, generation);
writes++; writes++;
+9 -7
View File
@@ -85,6 +85,7 @@ namespace {
o.push_back(Option("Pawn Structure (Endgame)", 100, 0, 200)); o.push_back(Option("Pawn Structure (Endgame)", 100, 0, 200));
o.push_back(Option("Passed Pawns (Middle Game)", 100, 0, 200)); o.push_back(Option("Passed Pawns (Middle Game)", 100, 0, 200));
o.push_back(Option("Passed Pawns (Endgame)", 100, 0, 200)); o.push_back(Option("Passed Pawns (Endgame)", 100, 0, 200));
o.push_back(Option("Space", 100, 0, 200));
o.push_back(Option("Aggressiveness", 100, 0, 200)); o.push_back(Option("Aggressiveness", 100, 0, 200));
o.push_back(Option("Cowardice", 100, 0, 200)); o.push_back(Option("Cowardice", 100, 0, 200));
o.push_back(Option("King Safety Curve", "Quadratic", COMBO)); o.push_back(Option("King Safety Curve", "Quadratic", COMBO));
@@ -96,8 +97,7 @@ namespace {
o.push_back(Option("King Safety X Intercept", 0, 0, 20)); o.push_back(Option("King Safety X Intercept", 0, 0, 20));
o.push_back(Option("King Safety Max Slope", 30, 10, 100)); o.push_back(Option("King Safety Max Slope", 30, 10, 100));
o.push_back(Option("King Safety Max Value", 500, 100, 1000)); o.push_back(Option("King Safety Max Value", 500, 100, 1000));
o.push_back(Option("Queen Contact Check Bonus", 4, 0, 8)); o.push_back(Option("Queen Contact Check Bonus", 3, 0, 8));
o.push_back(Option("Rook Contact Check Bonus", 2, 0, 4));
o.push_back(Option("Queen Check Bonus", 2, 0, 4)); o.push_back(Option("Queen Check Bonus", 2, 0, 4));
o.push_back(Option("Rook Check Bonus", 1, 0, 4)); o.push_back(Option("Rook Check Bonus", 1, 0, 4));
o.push_back(Option("Bishop Check Bonus", 1, 0, 4)); o.push_back(Option("Bishop Check Bonus", 1, 0, 4));
@@ -120,11 +120,11 @@ namespace {
o.push_back(Option("Full Depth Moves (non-PV nodes)", 3, 1, 100)); o.push_back(Option("Full Depth Moves (non-PV nodes)", 3, 1, 100));
o.push_back(Option("Threat Depth", 5, 0, 100)); o.push_back(Option("Threat Depth", 5, 0, 100));
o.push_back(Option("Selective Plies", 7, 0, 10)); o.push_back(Option("Selective Plies", 7, 0, 10));
o.push_back(Option("Null driven IID", false));
o.push_back(Option("Futility Pruning (Main Search)", true)); o.push_back(Option("Futility Pruning (Main Search)", true));
o.push_back(Option("Futility Pruning (Quiescence Search)", true)); o.push_back(Option("Futility Pruning (Quiescence Search)", true));
o.push_back(Option("Futility Margin 0", 50, 0, 1000)); o.push_back(Option("Futility Margin (Quiescence Search)", 50, 0, 1000));
o.push_back(Option("Futility Margin 1", 100, 0, 1000)); o.push_back(Option("Futility Margin Scale Factor (Main Search)", 100, 0, 1000));
o.push_back(Option("Futility Margin 2", 300, 0, 1000));
o.push_back(Option("Maximum Razoring Depth", 3, 0, 4)); o.push_back(Option("Maximum Razoring Depth", 3, 0, 4));
o.push_back(Option("Razoring Margin", 300, 150, 600)); o.push_back(Option("Razoring Margin", 300, 150, 600));
o.push_back(Option("LSN filtering", false)); o.push_back(Option("LSN filtering", false));
@@ -176,14 +176,16 @@ namespace {
template<typename T> template<typename T>
T get_option_value(const std::string& optionName) { T get_option_value(const std::string& optionName) {
T ret; T ret = T();
Options::iterator it = option_with_name(optionName); Options::iterator it = option_with_name(optionName);
if (it != options.end()) if (it != options.end())
{ {
std::istringstream ss(it->currentValue); std::istringstream ss(it->currentValue);
ss >> ret; ss >> ret;
} } else
assert(false);
return ret; return ret;
} }