├── Copying.txt ├── README.md ├── pgn ├── GM_games.pgn ├── ambiguous.pgn ├── bali02.pgn ├── chessdoctor.pgn ├── d00_chess_informant.pgn ├── electronic_campfire.pgn ├── europe_echecs.pgn ├── exeter_lessons_from_tal.pgn ├── famous_games.pgn ├── great_masters.pgn ├── hartwig.pgn ├── hayes.pgn ├── human_computer.pgn ├── immortal_games.pgn ├── kramnik.pgn ├── middleg.pgn ├── moscow64.pgn ├── newyork1924.pgn ├── perle.pgn ├── polgar.pgn ├── pon_korch.pgn ├── romero.pgn ├── russian_chess.pgn ├── scarborough_2001.pgn ├── scca.pgn ├── schiller.pgn └── semicomm.pgn └── src ├── Makefile ├── benchmark.cpp ├── bitbase.cpp ├── bitboard.cpp ├── bitboard.h ├── endgame.cpp ├── endgame.h ├── evaluate.cpp ├── evaluate.h ├── json.hpp ├── main.cpp ├── material.cpp ├── material.h ├── misc.cpp ├── misc.h ├── movegen.h ├── movepick.cpp ├── movepick.h ├── parser.cpp ├── pawns.cpp ├── pawns.h ├── position.cpp ├── position.h ├── psqt.cpp ├── scout.cpp ├── scoutfish.py ├── search.cpp ├── search.h ├── syzygy ├── tbprobe.cpp └── tbprobe.h ├── test.py ├── thread.cpp ├── thread.h ├── thread_win32.h ├── timeman.cpp ├── timeman.h ├── tt.cpp ├── tt.h ├── types.h ├── uci.cpp ├── uci.h └── ucioption.cpp /README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | Run powerful and flexible queries on **very big chessdatabases** and with **very high speed**. 4 | 5 | Start building an index out of a [PGN](https://en.wikipedia.org/wiki/Portable_Game_Notation) file: 6 | 7 | ./scoutfish make my_big_db.pgn 8 | 9 | Scoutfish will create a file called _my_big_db.scout_ with the needed bits to make 10 | the queries lightning fast. Queries are written in [JSON](https://en.wikipedia.org/wiki/JSON) 11 | format that is human-readable, well supported in most languages and very simple. 12 | Search result will be in JSON too. 13 | 14 | You can run Scoutfish from the command line: 15 | 16 | ./scoutfish scout my_big_db.scout { "sub-fen": "8/8/p7/8/8/1B3N2/8/8" } 17 | 18 | To find all the games that match the given **sub-fen** condition, i.e. all the 19 | games with at least one position with a black pawn in _a6_, a white bishop in 20 | _b3_ and a white knight in _f3_. Output will be like: 21 | 22 | ~~~~ 23 | { 24 | "moves": 14922, 25 | "match count": 8, 26 | "moves/second": 3730500, 27 | "processing time (ms)": 4, 28 | "matches": 29 | [ 30 | { "ofs": 75129, "ply": [11] }, 31 | { "ofs": 80890, "ply": [11] }, 32 | { "ofs": 342346, "ply": [13] }, 33 | { "ofs": 346059, "ply": [13] }, 34 | { "ofs": 375551, "ply": [21] }, 35 | { "ofs": 484182, "ply": [29] }, 36 | { "ofs": 486999, "ply": [29] }, 37 | { "ofs": 536474, "ply": [13] } 38 | ] 39 | } 40 | ~~~~ 41 | 42 | After some header, there is a list of matches, each match reports an offset 43 | (in bytes) in the original _my_big_db.pgn_ file, pointing at the beginning of 44 | the matching game and the ply number: this is the number of (half) moves before 45 | reaching the first position in the game that satisfies the given condition. 46 | 47 | In case you call Scoutfish from a higher level tool, like a GUI or a web interface, 48 | it is better to run in interactive mode: 49 | 50 | ~~~~ 51 | ./scoutfish 52 | setoption name threads value 8 53 | scout my_big_db.scout { "sub-fen": "8/8/8/8/1k6/8/8/8", "material": "KBNKP" } 54 | scout my_big_db.scout { "white-move": "O-O-O" } 55 | quit 56 | ~~~~ 57 | 58 | Scoutfish is strictly derived from [Stockfish](https://stockfishchess.org/) so, 59 | yes, it understands [UCI commands](http://wbec-ridderkerk.nl/html/UCIProtocol.html), 60 | like _setoption_, that we use to increase thread number according to our hardware: 61 | the search speed will increase accordingly! 62 | 63 | Above examples show how to look for a specific **material distribution** and **move** 64 | and how to compose a **multi-rule condition**: a position should satisfy all the rules 65 | to match the condition. 66 | 67 | You are not limited to search for a single _sub-fen_, the following condition: 68 | 69 | { "sub-fen": ["8/8/8/q7/8/8/8/8", "8/8/8/r7/8/8/8/8"] } 70 | 71 | Will find all the positions with a black queen **or** a black rook in a5. There 72 | is no limit to the size of the _sub-fen_ list, enabling to compose very powerful 73 | requests. 74 | 75 | The position **full FEN** is just a special _sub-fen_, so: 76 | 77 | { "sub-fen": ["rnbqkbnr/pp1p1ppp/2p5/4p3/3PP3/8/PPP2PPP/RNBQKBNR", 78 | "rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R"] } 79 | 80 | Will search for all the games with a _Caro-Kann_ or a _Sicilian_ opening. 81 | 82 | 83 | ## Rules 84 | 85 | Rules allow to look for very specific occurrences in the game. We have already 86 | seen some of them, like _sub-fen_, but there are many more. 87 | 88 | 89 | ##### result 90 | 91 | Find all games with a given result in PGN notation, like "1-0" or "1/2-1/2". 92 | Support lists. 93 | 94 | { "sub-fen": "8/pp1p1ppp/2p5/4p3/3PP3/8/PPP2PPP/8", "result": "1-0" } 95 | { "result": ["1-0", "0-1"] } 96 | 97 | To find all white winning games with given sub-fen or all the decided games. 98 | 99 | 100 | ##### result-type 101 | 102 | Find all games with a given result type that can be "mate" or "stalemate". 103 | 104 | { "result": "0-1", "result-type": "mate" } 105 | 106 | To find all games won by black by giving mate. 107 | 108 | 109 | ##### material 110 | 111 | Find all games with a given material distribution, i.e. the given pieces, 112 | no matter where placed. Support lists. 113 | 114 | { "material": ["KBNKNN", "KBNPKNN"] } 115 | 116 | To find all end-games with white Knight and Bishop plus one optional pawn 117 | against two black Knights. 118 | 119 | 120 | ##### imbalance 121 | 122 | Find all games with a given material imbalance. Support lists. 123 | 124 | { "imbalance": ["PPPv", "PPv"] } 125 | { "imbalance": "PPvN" } 126 | 127 | 128 | To find all games with 3 or 2 pawns advantage for white and all games 129 | where white is above 2 pawns but down of a knight. 130 | 131 | 132 | ##### sub-fen 133 | 134 | Find all games with a position matching the sub-fen pattern given in 135 | PGN notation. Support lists. 136 | 137 | { "sub-fen": "1n2k1n1/8/8/8/8/8/8/2B1K1N1", "material": "KBNKNN" } 138 | 139 | To find all games with given sub-fen **and** given material distribution. 140 | Note that the sub-fen is matched by the start position and without the 141 | added rule on material, any game would match. A condition composed by 142 | sub-fen + material, can be used to find an **exact fen**. 143 | 144 | 145 | ##### white-move / black-move 146 | 147 | Find all games with a given move in PGN notation. Support lists. 148 | 149 | {"white-move": "e8=Q"} 150 | {"black-move": ["O-O-O", "O-O"]} 151 | {"black-move": "Rac1"} 152 | 153 | To find all games with white's queen promotion in _e8_ and all games 154 | with a black castling, no matter if long or short. Rule supports 155 | SAN notation with a disambiguation, like _Rac1_. 156 | 157 | 158 | ##### moved / captured 159 | 160 | Find all games with a given moved and/or captured piece. Pieces are listed 161 | in a single string. 162 | 163 | {"moved": "KP", "captured": "Q" } 164 | {"captured": "" } 165 | 166 | To find all games with a queen captured by a king or a pawn and all games 167 | with a quiet move (eventually to be used in a multi-rule condition). 168 | 169 | 170 | ##### stm 171 | 172 | This rule matches the given side to move, that can be "white" or "black" 173 | Usually it is used in a multi-rule condition. 174 | 175 | {"stm": "black", "captured": "QR" } 176 | 177 | To find all games where black side captures a queen or a rook. 178 | 179 | 180 | ##### pass 181 | 182 | This rule matches any position. It is used mainly for debugging purposes or 183 | as a part of a more complex condition (see streaks). 184 | 185 | {"pass": "" } 186 | 187 | To find the number of games in the DB, because it will match any game. 188 | 189 | 190 | ## Sequences 191 | 192 | A _sequence_ is a powerful feature of Scoutfish to look for games that satisfy 193 | more than one condition at *different times in game*. This is very useful in 194 | looking for a piece path from a position. Typical tournament player questions 195 | are: how does one maneuver the bishop in this opening, should we go f1-d3-c2 or 196 | f1-b5-a4. When should we select which maneuver? 197 | 198 | { "sequence": [ { "sub-fen": "r1bqkb1r/pppp1ppp/2n2n2/1B2p3/4P3/2N2N2/PPPP1PPP/R1BQK2R", "result": "1-0" }, 199 | { "sub-fen": "8/8/8/8/2B5/8/8/8" }, 200 | { "sub-fen": "8/8/8/8/8/5B2/8/8" } ] } 201 | 202 | The above _sequence_ will find all the games won by white, with _Four Knights 203 | Spanish Variation_ opening and with bishop maneuvering to b5-c4-f3. 204 | 205 | A _sequence_ is mainly a list of conditions: a game should match first condition, 206 | then the second one (eventually later in the game) and so on for all the list, 207 | that can be arbitrary long. Output of the search will be like: 208 | 209 | ~~~~ 210 | { 211 | "moves": 28796, 212 | "match count": 2, 213 | "moves/second": 5759200, 214 | "processing time (ms)": 5, 215 | "matches": 216 | [ 217 | { "ofs": 19054, "ply": [7, 15, 17] }, 218 | { "ofs": 20653, "ply": [7, 15, 17] } 219 | ] 220 | } 221 | ~~~~ 222 | 223 | Where _ply_ list will show the matching ply for each condition in the sequence. 224 | 225 | 226 | ## Streaks 227 | 228 | A _streak_ is a special kind of sequence. It is defined like a sequence and has 229 | all the sequence properties, but it is different in two key points: 230 | 231 | - Conditions in a streak should be satisfied in consecutive (half) moves 232 | - A streak can appear nested in a bigger, outer sequence 233 | 234 | Mainly a streak is like a sequence but with the added constrain that the conditions 235 | should be satisfied one-by-one along consecutive moves. You may want to use a 236 | streak to look for a white's pawn-down imbalance that should persist for at least 237 | few moves to be sure we are not in the middle of a capture-recapture combination: 238 | 239 | ~~~~ 240 | { "streak": [ { "imbalance": "vP" }, { "imbalance": "vP" }, { "imbalance": "vP" } ] } 241 | ~~~~ 242 | 243 | From chess perspective, say you want to find games with a clearance sacrifice in 244 | the Benoni for white. Plan of e5, dxe5, followed by f5 and then Ne4 for white. 245 | The first three moves are in a streak, but the last move might be delayed by a 246 | move (but is also played immediately): 247 | 248 | ~~~~ 249 | { "sequence": [ { "sub-fen": "rnbqkb1r/pp1p1ppp/4pn2/2pP4/2P5/2N5/PP2PPPP/R1BQKBNR"}, 250 | { "streak": [ { "white-move": "e5"}, { "black-move": "dxe5"}, { "white-move": "f5"} ] }, 251 | { "white-move": "Ne4"} ] } 252 | ~~~~ 253 | 254 | The above sequence, first checks for Benoni opening, then checks for the 255 | **consecutives** e5, dxe5, f5 then finally by the (possibly delayed) Ne4. 256 | 257 | Some rules like _captured_ are very suitable to be used in a streak: 258 | 259 | ~~~~ 260 | { "streak": [ { "captured": "" }, { "stm": "white", "captured": "Q" }, { "captured": "" } ] } 261 | { "streak": [ { "white-move": "e5"}, { "pass": "" }, { "white-move": "f5" } ] } 262 | ~~~~ 263 | 264 | To find all games where white captures a net queen, i.e. not in a capture-recapture 265 | combination. The second streak uses 'pass' rule to find all games with white _e5_ 266 | followed by _f5_, independently from the black reply. 267 | 268 | 269 | ## Python wrapper 270 | 271 | As a typical UCI chess engine, also Scoutfish is not intended to be exposed to the 272 | user directly, eventually a GUI or a web interface will handle the user interaction, 273 | composing the query and later presenting the results in a graphical form, ensuring 274 | a user friendly experience. 275 | 276 | To easy integration with higher level tools, a Python wrapper is provided through 277 | **scoutfish.py** file: 278 | 279 | ~~~~python 280 | from scoutfish import Scoutfish 281 | 282 | p = Scoutfish() 283 | p.setoption('threads', 4) # Will use 4 threads for searching 284 | p.open('my_big.pgn') 285 | 286 | q = {'white-move': 'O-O-O'} # Our query, defined as a simple dict 287 | result = p.scout(q) 288 | 289 | num = result['match count'] 290 | print('Found ' + str(num) + ' games') 291 | 292 | # Run again the query but limiting output to max 10 games and skipping 293 | # the first 50 results. This is useful for pagination. 294 | q = {'skip': 50, 'limit': 10, 'white-move': 'O-O-O'} 295 | 296 | result = p.scout(q) 297 | games = p.get_games(result['matches']) # Load the pgn games from my_big.pgn 298 | 299 | for g in games: 300 | print(g['pgn']) 301 | 302 | p.close() 303 | ~~~~ 304 | -------------------------------------------------------------------------------- /pgn/ambiguous.pgn: -------------------------------------------------------------------------------- 1 | [Event "?"] 2 | [White "Spassky"] 3 | [Black "Petrosian"] 4 | [Date "1969.??.??"] 5 | [Result "1-0"] 6 | 7 | {Queen's Gambit Declined (by transposition), 30 moves. Spassky plays an excellent opening and emerges with an advantage. Petrosian, under pressure, allows Spassky to obtain a passed d pawn which decides the game. Petrosian was world champion from 1963-1969; Spassky from 1969-1972.} 8 | 1. c4 {The English opening. White takes possession of d5.} 9 | 1... Nf6 {Developing and attacking the center.} 10 | 2. Nc3 {A natural move.} 11 | 2... e6 {Preparing to play 3... d5. Also playable is 2... e5.} 12 | 3. Nf3 13 | 3... d5 14 | 4. d4 {This position can also arise from the Queen's gambit as follows: 1. d4 d5 2. c4 e6 3. Nc3 Nf6 4. Nf3.} 15 | 4... c5 {This variation is called the Semi-Tarrasch Defense.} 16 | 5. cxd5 {If 5... exd5 then Black will end up with an isolated queen pawn.} 17 | 5... Nxd5 {The usual next move for White when this game was played was 6. e3} 18 | 6. e4 $1 {Spassky will demonstrate the strength of the pawn center.} 19 | 6... Nxc3 20 | 7. bxc3 21 | 7... cxd4 22 | 8. cxd4 {White now occupies the center. The question is whether he will be able to use it to advantage or spend all his energy protecting it.} 23 | 8... Bb4+ {The side with less space should strive to exchange pieces.} 24 | 9. Bd2 25 | 9... Bxd2+ 26 | 10. Qxd2 27 | 10... 0-0 28 | 11. Bc4 {A natural developing move.} 29 | 11... Nc6 {Developing and beginning to put pressure on White's center.} 30 | 12. 0-0 31 | 12... b6 {The bishop is better placed on b7 then d7.} 32 | 13. Rd1 33 | 13... Bb7 34 | 14. Rfe1 {Developing the last piece.} 35 | 14... Rc8 {Placing the rook on the open file.} 36 | 15. d5 $1 {If here 15... Na5 then Spassky planned 15. Bd3 exd5 16. e5 with a strong attack. White's bishop, knight, and queen all are ready for a kingside attack while Black's pieces are on the other wing.} 37 | 15... exd5 38 | 16. Bxd5 {White's pieces are now very active and it is difficult for Black to find a good defense.} 39 | 16... Na5? {The knight ends up being out of the game. Either 16... Qe7 or 16... Qc7 is better.} 40 | 17. Qf4 $1 {The queen is well placed here, exerting pressure on f7. The threat is Bxf7+ winning the queen.} 41 | 17... Qc7 42 | 18. Qf5 {threatening Ng5. If now 18... h6 then 19. Bxb7 Qxb7 20. Rd7 Rc7 21. Red1 and White's rook on the seventh rank gives him a distinct advantage.} 43 | 18... Bxd5 {This gives White a very strong passed d pawn.} 44 | 19. exd5 45 | 19... Qc2 {White would have a good game after 20. Qxc2 Rxc2 21. Re7 Rxa2 22. Rxa7} 46 | 20. Qf4 {White wants to force a win in the middle game rather than play out a long end game.} 47 | 20... Qxa2 48 | 21. d6 {Nimzovich often spoke of a past pawn's lust to expand.} 49 | 21... Rcd8 {Covering the pawn's queening square.} 50 | 22. d7 51 | 22... Qc4 52 | 23. Qf5 {White threatens 24. Ng5 as well as 24. Rc1 followd by 25. Rc7} 53 | 23... h6 54 | 24. Rdc1 {Now the rook will get to the seventh rank.} 55 | 24... Qa6 56 | 25. Rc7 57 | 25... b5 {Giving the queen a better chance to defend.} 58 | 26. Nd4 59 | 26... Qb6 60 | 27. Rc8 $1 {Black cannot play 27... Qxd4 because of 28. Rxd8 Rxd8 29. Re8+ Rxe8 30. dxe8=Q#. White threatens 28. Re8 Qxd4 29. Rxf8+ Rxf8 29. Rxf8 Kxf8 30. Qc5+ Qxc5 31. d8=Q#.} 61 | 27... Nb7 {Guarding the rook on d8.} 62 | 28. Nc6 $1 63 | 28... Nd6 64 | 29. Nxd8 $1 {A decisive finish.} 65 | 29... Nxf5 66 | 30. Nc6 {Black resigned since White is threatening both 31. Rxf8+ Kxf8 32. Re8# and 31. Rxf8+ Kxf8 32. d8=Q+ Qxd8 33. Nxd8} 67 | 1-0 68 | 69 | 70 | [Event "1st/2nd Prize Corus"] 71 | [Site "?"] 72 | [Date "2008.??.??"] 73 | [Round "?"] 74 | [White "Bazlov, Y. (wh)"] 75 | [Black "[+4146.21c6f6 (bl)"] 76 | [Result "1-0"] 77 | [SetUp "1"] 78 | [FEN "2n1n3/3Qp1RP/2Kb1k1P/8/3q4/6B1/8/8 w - - 0 1"] 79 | 80 | 1. Bh4+ Qxh4 2. Rf7+ Kxf7 3. Qf5+ Qf6 4. Qxf6+ exf6 5. h8=Q Bf8 6. h7 Ne7+ 81 | 7. Kd7 Ng7 8. Qg8+ Nxg8 9. h8=N# 1-0 82 | 83 | [Event "IPCCC Paderborn"] 84 | [Date "2006.12.27"] 85 | [White "Ikarus V0.40a"] 86 | [Black "Jonny 2.90"] 87 | [Round "2"] 88 | [Result "0-1"] 89 | 90 | 1.d4 Nf6{(g8f6 tb=0/0)} 2.c4{00:08.078} e6{(e7e6 tb=0/0)} 3.Nf3{00:06.156} 91 | b6{(b7b6 tb=0/0)} 4.g3{00:06.485} Ba6{(c8a6 tb=0/0)} 5.Nbd2{00:06.094} 92 | d5{(d7d5 tb=0/0)} 6.Bg2{00:06.188} Be7{(f8e7 tb=0/0)} 7.O-O{00:07.656} 93 | O-O{(e8g8 tb=0/0)} 8.b3{00:05.985} c5{(c7c5 c1b2 b8c6 d4c5 e7c5 e2e3 94 | tb=0/0) 0.27/15 01:45.094} 9.dxc5{00:08.984} Bxc5{(e7c5 c1b2 b8c6 e2e3 95 | a8c8 d1c2 c8c7 f1d1 c6b4 c2b1 tb=0/0) 0.25/15 00:53.594} 10.Bb2{00:08.172} 96 | Nc6{(b8c6 d1c2 a8c8 e2e3 f6g4 tb=0/0) 0.13/15 03:04.281} 11.Rc1{04:36.468} 97 | Rc8{(a8c8 a2a3 c5e7 c4d5 f6d5 d2e4 d8d7 d1d2 f7f6 f1d1 c6a5 f3d4 e6e5 98 | d4f5 d7f5 d2d5 g8h8 tb=0/0) 0.17/15 02:58.671} 12.a3{11:18.844} Be7 13.cxd5{02:56.969} 99 | Nxd5{(f6d5 d2c4 d5f6 f3g5 d8d1 f1d1 f8d8 d1d8 c6d8 e2e3 f6e8 g5e4 b6b5 100 | c4e5 c8c1 b2c1 f7f6 e5d3 a6b7 c1b2 e6e5 tb=0/0) 0.13/17 02:24.437} 14.b4{04:16.141} 101 | Bf6{(e7f6 b2f6 d8f6 d1a4 a6b7 d2e4 f6e7 e2e3 f7f5 e4d2 e6e5 f1d1 e5e4 a4b3 102 | c6b4 c1c8 f8c8 f3d4 c8c3 d4f5 e7f8 tb=0/0) 0.16/15 02:47.359} 15.Bxf6{01:02.516} 103 | Qxf6{(d8f6 d1a4 a6b7 d2e4 f6e7 e2e3 tb=0/0) 0.10/16 00:27.781} 16.Qa4{03:26.047} 104 | Bb7{(a6b7 d2e4 f6e7 f1d1 f8d8 e2e3 a7a5 b4b5 c6a7 a4b3 c8c1 d1c1 d5f4 e3f4 105 | b7e4 tb=0/0) 0.06/17 00:29.610} 17.Ne4{03:48.172} Qe7{(f6e7 f1d1 f8d8 106 | e2e3 f7f5 e4d2 e7f6 d2c4 d5c3 d1d8 c8d8 a4c2 c3e4 b4b5 c6e7 c1d1 e7g6 d1d8 107 | f6d8 tb=0/0) 0.11/17 00:07.109} 18.Qb3{02:30.703} Nf6{(d5f6 e4f6 e7f6 108 | f1d1 f8d8 b3a4 tb=0/0) 0.09/16 03:30.641} 19.Nxf6{01:29.938} Qxf6{(e7f6 109 | f1d1 f8d8 b3a4 a7a6 d1d8 f6d8 a4d1 d8d1 c1d1 c6b8 f3e1 b7g2 g1g2 g8f8 e2e4 110 | f8e7 f2f4 f7f6 g2f3 tb=0/0) 0.00/17 00:19.312} 20.Rfd1{02:03.813} Rfd8 111 | 21.b5{02:10.078} Na5{(c6a5 c1c8 d8c8 b3d3 f6e7 h2h4 f7f6 g2h3 f6f5 f3e5 112 | e7c5 e5f3 c5c7 f3g5 c7e7 g5e6 e7e6 h3f5 e6e8 f5c8 e8c8 tb=0/0) 0.07/18 113 | 02:45.047} 22.Rxc8{03:30.359} Rxc8 23.Qd3{01:23.157} Qe7{(f6e7 f3e5 114 | b7g2 g1g2 f7f6 e5f3 a5c4 d1c1 e7c5 g2g1 c5d5 f3d4 c4d6 c1c8 d6c8 e2e4 d5a2 115 | f2f4 g8f7 d4c6 tb=0/0) 0.03/18 01:03.781} 24.h4{05:22.891} f6{(f7f6 g2h3 116 | b7d5 e2e4 d5c4 d3d7 c8c7 d7e7 c7e7 f3d4 g8f7 d1c1 e7e8 g1g2 e6e5 d4c6 c4b5 117 | c6a7 b5c4 a7c6 c4b3 c6a5 b6a5 tb=0/0) 0.16/17 01:03.906} 25.Bh3{01:47.359} 118 | Bd5 26.e4{02:17.562} Bc4 27.Qd7{01:31.610} Rc7{(c8c7 d7e7 c7e7 f3d4 119 | e7d7 h3g4 g8f7 g1g2 d7c7 d1c1 c7c5 c1d1 f7e7 tb=0/0) 0.40/19 01:26.312} 120 | 28.Qxe7{01:26.797} Rxe7{(c7e7 f3d4 e7d7 h3g4 g8f7 f2f4 a5b7 e4e5 b7c5 121 | d4c6 d7c7 c6d8 f7g6 d1d6 c4b5 h4h5 g6h6 e5f6 tb=0/0) 0.47/20 00:40.344} 122 | 29.Nd4{03:57.594} Rd7 30.Bg4{04:58.703} Kf7{00:00.016} 31.a4{01:50.937} 123 | Rd6{(d7d6 f2f4 e6e5 f4e5 f6e5 d4f5 d6d1 g4d1 c4e6 g3g4 g7g6 f5e3 a5b3 124 | g1f2 b3d2 d1c2 f7e7 f2e2 d2b3 c2d1 e7d6 h4h5 tb=0/0) 0.75/19 00:58.469} 125 | 32.f4{00:33.891} e5{(e6e5 f4e5 f6e5 d4f5 d6d1 g4d1 c4e6 g3g4 g7g6 f5e3 126 | a5b3 g1f2 b3c5 f2f3 f7e7 h4h5 e7d6 h5g6 h7g6 e3f1 d6c7 tb=0/0) 0.75/20 127 | 00:43.890} 33.fxe5{02:11.844} fxe5 34.Nf5{05:07.594} Rxd1 35.Bxd1{00:07.031} 128 | g6{(g7g6 f5e3 c4d3 d1c2 d3c2 e3c2 f7e6 c2e3 e6d6 g1f2 d6c5 e3g4 a5c4 g4f6 129 | c5d4 f6h7 d4e4 h7f8 e4f5 f8d7 e5e4 d7b8 f5g4 tb=2/180) 1.05/24 04:12.500} 130 | 36.Nd6{00:51.312} Ke7{(f7e7 d6c8 e7d7 c8a7 c4d3 g1f2 d3e4 f2e3 e4f5 tb=0/46) 131 | 1.04/22 01:06.078} 37.Nxc4{06:39.813} Nxc4{(a5c4 d1b3 c4d2 b3g8 d2e4 132 | g8h7 e7f6 h4h5 e4g3 h5g6 g3e2 g1f2 e2f4 g6g7 f6g7 h7c2 g7f6 f2e3 f4e6 e3e4 133 | e6d4 c2d3 f6e6 tb=83/1699) 1.31/24 00:55.156} 38.Bb3{00:44.406} Nd2{(c4d2 134 | b3g8 d2e4 g8h7 e7f6 h4h5 e4g3 h5g6 g3e2 g1f2 e2f4 g6g7 f6g7 h7c2 f4e6 f2e3 135 | e6c5 e3d2 g7f6 d2c3 e5e4 c3d4 f6f5 tb=297/5465) 1.42/24 00:37.343} 39.Bg8{01:43.922} 136 | Nxe4 40.Bxh7{01:40.906} Kf6{(e7f6 h4h5 e4g3 h5g6 g3h5 g1f2 h5f4 f2e3 f4g6 137 | h7g8 g6f4 tb=6847/118788) 1.50/26 01:44.750} 41.h5{01:19.797} Nxg3{(e4g3 138 | h7g6 f6g5 g6f7 g3h5 g1f2 h5f6 f2e3 g5f5 f7b3 e5e4 e3d4 f5f4 b3d1 e4e3 d4d3 139 | tb=31062/1523303) 1.83/27 02:40.875} 42.Bxg6{02:30.735} Kg5{(f6g5 g6f7 140 | g3h5 g1f2 h5f6 f2e3 g5f5 f7c4 e5e4 c4b3 f6g4 e3e2 f5f4 b3c2 e4e3 e2f1 tb=31443/572368) 141 | 2.05/27 00:05.312} 43.Kf2{00:22.750} Nxh5{(g3h5 g6e4 g5f4 tb=17572/395628) 142 | 2.30/26 01:58.766} 44.Bf7{00:43.047} Nf6{(h5f6 f2f3 g5f5 f7a2 tb=4635/75796) 143 | 2.42/24 00:47.078} 45.Ke3{01:05.422} Kf5{(g5f5 f7c4 e5e4 c4a2 f5e5 tb=5514/84196) 144 | 2.71/24 00:56.922} 46.Bc4{02:05.907} e4{(e5e4 c4b3 f5e5 tb=29093/437627) 145 | 2.93/25 02:15.218} 47.Ke2{01:13.954} Ke5{(f5e5 c4a2 e5d4 a2b3 tb=14097/268343) 146 | 3.37/23 00:57.609} 48.Kd2{00:47.703} Nd5{(f6d5 c4b3 e5d4 b3c2 d5e3 c2b3 147 | e3c4 d2e2 c4b2 b3c2 e4e3 c2b3 b2d3 b3c2 d3f4 e2d1 tb=14100/212997) 3.41/23 148 | 01:25.500} 49.Bb3{00:32.875} Kd4{(e5d4 b3c2 d5e3 c2b3 e3c4 d2e2 c4b2 149 | b3c2 tb=30403/361794) 3.41/24 01:15.188} 50.Bd1{00:47.812} e3{(e4e3 d2e1 150 | d4d3 d1g4 d3e4 tb=21892/263876) 3.63/24 01:34.687} 51.Ke1{02:43.969} 151 | Nf4{(d5f4 d1g4 d4c3 tb=85865/1117783) 3.88/24 02:13.937} 52.Bc2{01:47.922} 152 | Kc3{(d4c3 c2e4 c3b3 e4b7 b3c2 b7c8 c2d3 c8f5 d3c4 f5d7 c4b4 tb=67090/749863) 153 | 3.97/25 05:16.907} 53.Be4{00:20.468} Kb3{(c3b3 e4b7 b3a4 tb=32182/425409) 154 | 4.97/24 06:00.625} 54.Bc6{01:04.359} Kxa4{(b3a4 c6e8 a4b4 e8d7 b4c3 d7g4 155 | e3e2 g4f5 tb=3639/67429) 5.70/20 00:40.344} 55.Bd7{00:08.922} Kb4{(a4b4 156 | e1d1 b4c3 d7f5 e3e2 d1e1 c3c4 e1d2 c4b5 f5d7 b5b4 d7e8 tb=7192/158536) 157 | 5.59/19 02:43.656} 56.Kd1{04:32.282} Kc3{00:41.578} 57.Bf5{01:11.859} 158 | e2{(e3e2 d1e1 c3c4 f5h7 c4b5 e1d2 a7a5 h7g8 a5a4 d2e1 b5b4 tb=13624/221488) 159 | 7.15/19 02:12.312} 58.Ke1{00:19.594} Kc4{(c3c4 f5h7 c4b5 tb=20578/289546) 160 | 7.40/19 01:54.078} 59.Kd2{02:27.266} Kxb5{(c4b5 f5d7 b5c5 tb=5528/84512) 161 | 10.66/19 04:32.297} 0-1 162 | -------------------------------------------------------------------------------- /pgn/d00_chess_informant.pgn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcostalba/scoutfish/00cec1339f97114a32c30080dbad5e3a500634f2/pgn/d00_chess_informant.pgn -------------------------------------------------------------------------------- /pgn/exeter_lessons_from_tal.pgn: -------------------------------------------------------------------------------- 1 | [Event "Olympic games"] 2 | [Site "Munchen"] 3 | [Date "1958"] 4 | [White "Walther"] 5 | [Black "Tal,M"] 6 | [Result "0-1"] 7 | [Source "Exeter Chess Club"] 8 | [Annotator "DrDave"] 9 | 10 | {Natural moves in the opening} 11 | 1. e4 c5 2. Ne2 Nf6 3. d3 Nc6 4. Nd2 12 | { 13 | So many words have been written about Tal that my own observations are perhaps rather superfluous. The brilliance of his play and the dramatic way his wins are often achieved are apparent to all. 14 | For tonight's session, and with their instructional content in mind, I would just like to add that it was interesting to me how some of his most striking wins are introduced by apprently simple means - not striving for complications but building up with classically good moves. This may be partly a reflection of how familiar the GM play of the 1950s is to a club player of the 1990s - the Modern Benoni was by no means a staple when Tal played it - but nonetheless you feel that Capablanca would have chosen many of Tal's moves, even if he would have tried to reach a decision by different means. 15 | Most curious. It is not unusual to develop one or other Knight to the second rank, but both at once, and before developing either Bishop, is very odd. Any advantage Walther saw in getting Tal out of the books is outweighed by the unnatural formation and the need to get his pieces untangled. Tal does not strike immediately, but develops quietly, waiting for a suitable moment to arise. 16 | } 17 | 4... d5 5. c3 dxe4 6. dxe4 g6 7. Qc2 Bg7 8. a4 $2 O-O 9. Nc4 $2 Na5 10. Nf4 18 | { 19 | White is trying to get sorted but is a long way from castling. Tal gives White's formation a sharp push, and after some wobbling, it collapses. 20 | } 21 | 10... e5 $1 11. Nd5 Nxe4 12. Qxe4 Nb3 13. Ra3 Bf5 14. Qe3 Nxc1 15. Qxc5 Re8 16. Qe3 22 | { 23 | On "body count" Black is a bare Pawn up, but as Bill Hartston says, its the pieces on the board that count, and White's are horribly uncoordinated. 24 | } 25 | 16... Bf8 17. Nb4 a5 18. Nxa5 Qxa5 19. Qxc1 Bxb4 20. cxb4 Qxb4+ 21. Rc3 Qe4+ 22. Be2 Rxa4 23. f3 Qb4 24. g4 e4 $1 26 | { 27 | Properly opening up lines against White's uncastled King. 28 | } 29 | 25. f4 Ra2 $1 26. gxf5 Rxb2 27. O-O $1 30 | { 31 | At last! But Black's nicely centralised and active forces are still in control. 32 | } 33 | 27... Rxe2 28. Rc8 Qb6+ 29. Kh1 Rxc8 30. Qxc8+ Kg7 31. fxg6 hxg6 32. f5 Qf6 33. Qxb7 Qe5 34 | 0-1 35 | 36 | [Event "ch-SU"] 37 | [Site "Riga"] 38 | [Date "1958"] 39 | [White "Averbakh"] 40 | [Black "Tal,M"] 41 | [Result "0-1"] 42 | [Source "Exeter Chess Club"] 43 | [Annotator "DrDave"] 44 | 45 | {Trying to refute an opponent's opening} 46 | 1. d4 Nf6 2. c4 e6 3. Nc3 c5 4. d5 exd5 5. cxd5 d6 6. e4 g6 7. Be2 Bg7 8. Nf3 O-O 9. O-O Re8 47 | { 48 | So far, so typical of the Modern Benoni. At the time this formation was still considered rather suspect (and maybe it still is in some circles), so Averbakh adopts a very active plan to 'punish' this cheeky opening, targeting d6. 49 | } 50 | 10. Qc2 Na6 11. Bf4 Nb4 12. Qb1 51 | { 52 | White's formation is just for the moment in need of a few moves for perfect comfort. Tal decides the time to strike is now! 53 | } 54 | 12... Nxe4 13. Nxe4 Bf5 14. Nfd2 Nxd5 55 | { 56 | This sacrifice of a Knight for central Pawns is seen repeatedly in Tal's play, and his practical results have been excellent. 57 | } 58 | 15. Bxd6?! 59 | (15. Bg3 {e.g.} 15... Qe7 16. Bf3 Rad8) 60 | 15... Nf6 16. Bf3 Nxe4 17. Nxe4 Bxe4 18. Bxe4 Qxd6 61 | { 62 | The scrapping has led to a winning position for Black. Opposite coloured Bishops only draw if they are the only things on the board! 63 | } 64 | 19. Qc2 Re7 20. Bf3 Rae8 21. Rad1 Bd4 22. a4 b6 23. b3 Re5 24. Rd2 h5 25. Re2 Rxe2 26. Bxe2 h4 27. Kh1 Qf4 28. g3 Qf6 29. Qd1 Rd8 30. Bg4 Bxf2 31. Qe2 Rd2 32. Qe8+ Kh7 33. gxh4 Qd4 34. Bh3 Qd3 35. Bg2 Rd1 65 | 0-1 66 | 67 | [Event ""] 68 | [Site "Riga"] 69 | [Date "1954"] 70 | [White "Saigin"] 71 | [Black "Tal,M"] 72 | [Result "0-1"] 73 | [Source "Exeter Chess Club"] 74 | [Annotator "DrDave"] 75 | 76 | {Natural moves in the middlegame} 77 | 1. d4 Nf6 2. c4 c5 3. Nf3 e6 4. g3 cxd4 5. Nxd4 d5 6. Bg2 e5 7. Nf3 d4 8. O-O Nc6 9. e3 $5 Be7 10. exd4 exd4 11. Nbd2 Be6 78 | { 79 | White has allowed Black a passed Pawn, hoping that it will become weak. 80 | } 81 | 12. Re1 O-O 13. b3 Qd7 14. Bb2 Rad8 15. a3 a5 16. Ne5 Nxe5 17. Rxe5 b6 18. Nf3 Bc5 19. Qd2 Ng4 20. Ree1 d3 21. Rf1 82 | { 83 | Black has a nice position, but what to do next? 84 | } 85 | 21... Qd6 86 | { 87 | "Such quiet and apparently non-constructive moves are among the finest and most difficult to find in a game of chess." - Clarke. 88 | } 89 | 22. Qc3 90 | (22. b4 axb4 23. axb4 Bxb4)(22. h3 Nxf2 23. Rxf2 Qxg3) 91 | 22... f6 23. Rad1 Rfe8 24. Rd2 Bf5 25. Ng5 92 | { 93 | Surely Black's position has reached its peak of potential. All we need now is a way of translating the good position into a decisive tactical blow - and of that art Tal has always been a master. 94 | } 95 | 25... Ne3 $3 26. fxe3 96 | (26. Re1 Nxg2 27. Rxe8+ Rxe8 28. Kxg2 Qc6+ 29. f3 97 | (29. Nf3 Be4) 98 | 29... Re1) 99 | 26... Bxe3+ 27. Kh1 100 | (27. Rff2 Bxf2+ 28. Kxf2 Qc5+ 29. Kf1 Re1+ 30. Kxe1 Qg1+ 31. Bf1 Re8+)(27. Rdf2 Bxf2+ 28. Rxf2 101 | (28. Kxf2 Qc5+) 102 | 28... d2 29. Bd5+ Qxd5 30. cxd5 d1=Q+) 103 | 27... Bxd2 28. Qxd2 Re2 29. Qc3 Rxg2 104 | (29... Rxg2 30. Kxg2 d2 31. Rd1 Bg4 32. Nf3 Qd3) 105 | 0-1 106 | 107 | [Event "23rd USSR ch"] 108 | [Site ""] 109 | [Date "1956"] 110 | [White "Tal,M"] 111 | [Black "Simagin"] 112 | [Result "1-0"] 113 | [Source "Exeter Chess Club"] 114 | [Annotator "DrDave"] 115 | 116 | {A real sacrifice} 117 | 1. e4 c6 2. d4 d6 3. Nc3 Nf6 4. f4 Qb6 5. Nf3 Bg4 6. Be2 Nbd7 7. e5 Nd5 8. O-O Nxc3 9. bxc3 118 | 9... e6 119 | (9... Bxf3 10. Bxf3 dxe5 11. fxe5 Nxe5 12. Ba3) 120 | 10. Ng5 Bxe2 11. Qxe2 h6 121 | 12. Nxf7 122 | { 123 | Easy to see, hard to play! Simagin undoubtedly expected this move and was deliberately inviting it, judging that Tal was bluffing. Bravery from both players, then! 124 | Spielmann calls this type of move a 'real' sacrifice as opposed to those sacrificial combinations where the hoped-for gain is clear and short- term. We have seen already a Tal sacrifice in the game against Averbakh where the omens may have been good but the precise justification was not obvious. 125 | } 126 | 12... Kxf7 13. f5 dxe5 14. fxe6+ Kxe6 15. Rb1 Qxb1 16. Qc4+ Kd6 17. Ba3+ Kc7 18. Rxb1 Bxa3 19. Qb3 Be7 20. Qxb7+ Kd6 127 | 21. dxe5+ (21. Rd1 $1 $18) Nxe5 22. Rd1+ Ke6 23. Qb3+ Kf5 24. Rf1+ 128 | 24... Ke4 129 | (24... Kg6 25. Qe6+ Bf6 26. Qf5+ Kf7 27. Qxe5) 130 | 25. Re1+ Kf5 26. g4+ Kf6 27. Rf1+ Kg6 28. Qe6+ Kh7 29. Qxe5 Rhe8 30. Rf7 Bf8 31. Qf5+ Kg8 32. Kf2 Bc5+ 33. Kg3 Re3+ 34. Kh4 Rae8 35. Rxg7+ Kxg7 36. Qxc5 R8e6 37. Qxa7+ Kg6 38. Qa8 Kf6 39. a4 Ke5 40. a5 Kd5 41. Qd8+ Ke4 42. a6 Kf3 43. a7 Re2 44. Qd3+ R6e3 45. Qxe3+ 131 | 1-0 132 | 133 | [Event "ch-SU"] 134 | [Site "Moskva"] 135 | [Date "1957"] 136 | [White "Aronson"] 137 | [Black "Tal,M"] 138 | [Result "0-1"] 139 | [Source "Exeter Chess Club"] 140 | [Annotator "DrDave"] 141 | 142 | {Mixing it} 143 | 1. d4 e6 2. c4 f5 3. Nf3 Nf6 4. Nc3 Be7 5. g3 O-O 6. Bg2 d6 7. O-O Qe8 8. Re1 Qg6 9. e4 fxe4 10. Nxe4 Nxe4 11. Rxe4 Nc6 12. Qe2 Bf6 13. Bd2 e5 14. dxe5 dxe5 15. Bc3 Bf5 16. Nh4 Bxh4 17. Rxh4 Rae8 144 | { 145 | Tal's position looks OK but White's position also has virtues. The game now enters a scrappy phase where Tal's formidable powers of imagination and calculation shine. 146 | } 147 | 18. Qe3 h6 19. b4 $1 Qf6 20. b5 Nd8 21. Bd5+ Kh8 22. f4 $2 exf4 $1 23. Qd2 148 | { 149 | Just when White might have thought he was getting somewhere... 150 | } 151 | 23... Qb6+ $1 24. Bd4 Qg6 25. Qxf4 152 | (25. Rxf4 Ne6 26. Bxe6 Bxe6) 153 | 25... Kh7 $1 26. Qxc7 154 | (26. Qd2) 155 | 26... Bb1 27. Be5 Ne6 28. Qd6 Qf5 29. Bf4 Ng5 30. Qb4 Be4 31. Bxe4 Rxe4 32. Rf1 Re2 33. Qd6 Rxa2 34. Qd5 Qc2 35. c5 Rd8! 36. Bd6 Re8 $1 156 | (36... Re8 37. Qf5+ Qxf5 38. Rxf5 Re1+ 39. Rf1 Nf3+) 157 | {time} 158 | 0-1 159 | 160 | [Event "sf-ch-SU"] 161 | [Site "Tbilisi"] 162 | [Date "1956"] 163 | [White "Tal,M"] 164 | [Black "Yukhtman"] 165 | [Result "1-0"] 166 | [Source "Exeter Chess Club"] 167 | [Annotator "DrDave"] 168 | 169 | {Outside passed Pawn} 170 | 1. e4 c5 2. Nf3 d6 3. d4 cxd4 4. Nxd4 Nf6 5. Nc3 a6 6. Bg5 Nbd7 7. Bc4 h6 8. Bxf6 Nxf6 9. Qe2 e6 10. a3 Be7 11. Bb3 Bd7 12. O-O Qa5 13. Rad1 Qh5 14. Qe1 171 | 14... g5 $2 15. e5 $1 Ng4 16. Nf3 Bc6 17. Bd5 $1 Nxe5 18. Nxe5 exd5 19. Nxd5 $1 Bxd5 20. Rxd5 dxe5 21. Rxe5 O-O 22. Rxe7 172 | { 173 | Peter Clarke remarks that drawing chances in major piece endings depend on (1) exposed opponent's King, (2) possession of an advanced or passed Pawn, and (3) weak enemy Pawns. Black has none of these! 174 | } 175 | 22... Rac8 23. Qe4 b5 24. Re1 Rcd8 25. c4 $1 bxc4 26. Qxc4 Qg6 27. Ra7 Rfe8 28. Rxe8+ Rxe8 29. g3 Re6 30. Kg2 Rf6 31. Ra8+ Kh7 32. Rd8 $1 176 | { 177 | Deliberately inviting... 178 | } 179 | 32... Rxf2+ 33. Kxf2 Qf6+ 34. Ke3 Qxd8 35. Qd3+ Qxd3+ 36. Kxd3 180 | { 181 | Tal had of course had foreseen this simple win: the outside passed Pawn wins. 182 | } 183 | 36... Kg6 37. Ke4 Kf6 38. g4 $1 Ke6 39. b4 h5 40. gxh5 f5+ 41. Kd4 Kf6 42. a4 g4 43. b5 184 | 1-0 185 | 186 | [Event ""] 187 | [Site ""] 188 | [Date "1956"] 189 | [White "Tal,M"] 190 | [Black "Lisitsin"] 191 | [Result "1-0"] 192 | [Source "Exeter Chess Club"] 193 | [Annotator "DrDave"] 194 | 195 | {Active King in the Ending} 196 | 1. e4 c5 2. Nf3 d6 3. d4 cxd4 4. Nxd4 Nf6 5. Nc3 g6 6. f4 Nc6 7. Nxc6 bxc6 8. e5 Nd7 9. exd6 exd6 10. Be3 Be7 11. Qf3 d5 12. O-O-O Bf6 13. Bd4 O-O 14. h4 Rb8 15. Qf2 Rb4 16. Bxf6 Nxf6 17. a3 Qb6 18. Qxb6 Rxb6 19. Na4 Rb7 20. Bd3 Nh5 21. Rhf1 Re7 197 | { 198 | How to save the f-pawn? 199 | } 200 | 22. f5 $1 201 | { 202 | Can't be done, but Tal gives it up for a high price - scrambled pawns. 203 | } 204 | 22... gxf5 23. Rfe1 Rfe8 24.Rxe7 Rxe7 25. Kd2 205 | { 206 | In Exeter we say "KUFTE!" (King Up For The Endgame!) 207 | } 208 | 25... Ng3 26. Kc3 f4 27. Kd4 Bf5 209 | { 210 | And there it is, nicely posted in the middle of a lot of weak Black pawns. 211 | } 212 | 28. Rd2 Re6 29. Nc5 Rh6 30. Ke5 $1 Bxd3 31. cxd3 Rxh4 32. Kd6 Rh6+ 33. Kc7 Nf5 34. Kb7 Nd4 35. Rf2 a5 36. Rxf4 Ne6 37. Rg4+ Kf8 213 | { 214 | The game is decided all in the position of the two Kings. 215 | } 216 | 38. Kxc6 $1 38... Nxc5+ 39. Kxc5 Re6 40. Kxd5 Rb6 41. b4 axb4 42. axb4 Ke7 217 | { 218 | Too late 219 | } 220 | 43. Kc5 Rf6 44. Rd4 Rf5+ 45. Kb6 Rf6+ 46. Kc7 Rf5 47. Re4+ Kf6 48. Kc6 Rf2 49. g4 h5 50. gxh5 Kg5 51. b5 f5 52. Rb4 f4 53. b6 f3 54. b7 221 | (54. b7 Rc2+ 55. Kd5 f2 56. b8=Q f1=Q 57. Qg3+ Kf6 58. Qe5+ Kf7 59. Rb7+ Rc7 60. Rxc7+ Kf8 61. Qh8#) 222 | 1-0 223 | 224 | [Event "WM"] 225 | [Site "Varna tt stud"] 226 | [Date "1958"] 227 | [White "Tal,M"] 228 | [Black "Djurasevic"] 229 | [Result "1-0"] 230 | [Source "Exeter Chess Club"] 231 | [Annotator "DrDave"] 232 | 233 | {Activity of the pieces} 234 | 1. e4 c5 2. Nf3 Nc6 3. d4 cxd4 4. Nxd4 Nf6 5. Nc3 d6 6. Bg5 e6 7. Qd2 a6 8. O-O-O h6 9. Bf4 Bd7 10. Nxc6 Bxc6 11. f3 Qb6 12. Bc4 O-O-O 13. Be3 Qc7 14. Qf2 Nd7 15. f4 b5 16. Be2 Qb7 17. a3 $1 Be7 $2 235 | { 236 | This apparently safe developing move is not to the point. 237 | } 238 | (17... Nf6) 239 | 18. Bf3 Nf6 19. e5 Ne4 20. Nxe4 Bxe4 21. Bxe4 Qxe4 240 | 22. Bb6 $1 Rd7 23. Rhe1 Qb7 24. exd6 Rxd6 25. Rxd6 Bxd6 26. Qd4 Bc7 241 | { 242 | White can grab the pawn on g7, but... Purdy talks about 'contempt for Pawns' - what is more important is to keep active. 243 | } 244 | 27. Bxc7 $1 245 | { 246 | "The most important feature of each phase of the game is the activity of the pieces." - Stean. 247 | } 248 | (27. Qxg7 Bxf4+ 28. Kb1 Re8 29. g3 {when Black's active pieces have counterplay.}) 249 | 27... Qxc7 28. Rd1 Rd8 250 | { 251 | Biting the bullet! 252 | } 253 | (28... Qc4 29. Qxc4+ bxc4 30. Rd4)(28... f6 29. Qd6)(28... Rg8 29. Rd3 Kb8 30. Rc3 Qd8 31. Qc5 Qd7 32. Qb6+ Qb7 33. Qd6+ Ka8 34. Rc6)(28... Kb8 29. Rd3 Rc8 30. Rc3 Qb7 31. Rxc8+ Qxc8 32. Qxg7) 254 | 29. Qxd8+ Qxd8 30. Rxd8+ Kxd8 31. Kd2 Kd7 32. Kd3 Kd6 255 | { 256 | The exchanges have led to a position in which Tal again has a Queen's-side majority, but this is by no means a simple win because of the "outside" candidate. The reason this position is won is because the White Queen's-side majority is 3:2 as opposed to 4:3. This simpler formation can yield a passed Pawn more quickly. 257 | } 258 | 33. c4 bxc4+ 259 | (33... Kc6 {passive defence} 34. Kd4 bxc4 35. Kxc4 Kd6 36. b4 Kc6 37. a4 Kb6 38. b5 axb5+ 39. axb5 g5 40. fxg5 hxg5 41. h3 f6 42. g3 f5 43. Kd3 Kxb5 44. h4)(33... e5 {counterattack} 34. fxe5+ Kxe5 35. cxb5 axb5 36. b3 Kd5 37. a4 bxa4 38. bxa4 Kc5 39. Ke4 Kb4 40. Ke5 Kxa4 41. Kd6 Kb5 42. Ke7) 260 | 34. Kxc4 e5 35. fxe5+ Kxe5 36. b4 f5 37. b5 axb5+ 38. Kxb5 261 | 38... f4 39. a4 g5 40. a5 g4 262 | (40... Kd6 41. Kc4 Kc6 42. Kd4 Kb5 43. Ke4 Kxa5 44. Kf5 Kb5 45. h3) 263 | 41. Kc4 $1 264 | (41. Kc4 $1 Ke4 265 | (41... f3 42. gxf3 gxf3 43. Kd3)(41... Kd6 42. Kd4) 266 | 42. a6 f3 43. a7 f2 44. a8=Q+) 267 | 1-0 268 | 269 | -------------------------------------------------------------------------------- /pgn/hartwig.pgn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcostalba/scoutfish/00cec1339f97114a32c30080dbad5e3a500634f2/pgn/hartwig.pgn -------------------------------------------------------------------------------- /pgn/human_computer.pgn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcostalba/scoutfish/00cec1339f97114a32c30080dbad5e3a500634f2/pgn/human_computer.pgn -------------------------------------------------------------------------------- /pgn/perle.pgn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcostalba/scoutfish/00cec1339f97114a32c30080dbad5e3a500634f2/pgn/perle.pgn -------------------------------------------------------------------------------- /pgn/romero.pgn: -------------------------------------------------------------------------------- 1 | [Event "FIDE Wch KO"] 2 | [Site "Moscow"] 3 | [Date "2001.12.05"] 4 | [Round "4.4"] 5 | [White "Ponomariov, R."] 6 | [Black "Morozevich, A."] 7 | [Result "1-0"] 8 | [ECO "C78"] 9 | [WhiteElo "2684"] 10 | [BlackElo "2742"] 11 | [Annotator "GM Alfonso Romero Holmes"] 12 | [PlyCount "88"] 13 | [EventDate "2001.11.27"] 14 | [Source "Mikhail Golubev"] 15 | [SourceDate "2002.01.08"] 16 | 17 | 1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 4. Ba4 Nf6 5. O-O Bc5 6. c3 b5 7. Bb3 d6 8. a4 18 | Bg4 (8... Rb8 9. axb5 axb5 10. d4 Bb6 11. Na3 Bg4 12. Nxb5 O-O) 9. h3 Bxf3 10. 19 | Qxf3 O-O 11. a5 $1 (11. d3 Na5 12. Bc2 b4 13. Nd2 Rb8) 11... Rb8 12. d3 Nd7 13. 20 | Nd2 (13. Be3 Kh8 14. Nd2 14... Ne7 {Kasparov-Anand, Wijk aan Zee 2001}) 13... 21 | Kh8 14. Kh1 Ne7 15. Qe2 { } 15... h6 (15... f5 16. Nf3 (16. exf5 Nxf5 17. Ne4)) 22 | 16. f4 Ng6 (16... exf4 17. d4 Ba7 18. Rxf4) 17. f5 Nf4 18. Qf3 18... Nf6 { } 23 | 19. Bc2 (19. Nb1 b4 (19... g5 20. fxg6 fxg6 21. d4 (21. g3 $5) 21... Ba7 22. g3 24 | ) 20. Bc4 d5 21. exd5 bxc3 22. bxc3) 19... g5 20. fxg6 fxg6 21. Nb3 21... Nd7 { 25 | } 22. Bxf4 $1 22... Rxf4 23. Qe2 Ba7 (23... Qh4 24. Rxf4 exf4 (24... Qxf4 25. 26 | Rf1 Qh4 26. Rf7) 25. d4 $1 (25. Nxc5 dxc5 26. Rf1 Nf6) 25... Ba7 26. Rf1 c6 27. 27 | Qf3 g5 28. e5 $1) 24. Rxf4 exf4 25. Qg4 g5 26. d4 $16 { } 26... Nf6 27. Qe6 Qf8 28 | 28. e5 Nh5 29. exd6 29... Qxd6 { } (29... cxd6 30. Qd7) 30. Qxd6 (30. Re1) 29 | 30... cxd6 31. Re1 Rf8 32. Re7 Bb8 33. Nd2 Ng7 (33... Nf6 34. Rb7 Re8 35. Nf3 30 | Nd5 36. Rh7+ Kg8 37. Rxh6 Re2 38. Rg6+ Kf8 39. Bb3 Ne7 40. Rf6+) 34. Nf3 Re8 31 | 35. Rb7 d5 36. Rb6 { } 36... h5 37. Bg6 Rc8 38. Nxg5 f3 39. gxf3 Bf4 40. h4 Bc1 32 | 41. Rxa6 Bxb2 42. Bf7 Ne8 (42... Nf5 43. Ra7 (43. Be6 Rf8 44. Ra7 Ng7) 43... 33 | Ng7 44. Bxd5 Bxc3 45. Nf7+ Kh7 46. Be4+ Kg8 47. Nd6) 43. Rg6 Nc7 44. Rc6 Ra8 34 | 1-0 35 | 36 | [Event "FIDE Wch KO"] 37 | [Site "Moscow"] 38 | [Date "2001.??.??"] 39 | [Round "5.5"] 40 | [White "Ivanchuk, V."] 41 | [Black "Lautier, J."] 42 | [Result "1-0"] 43 | [ECO "E15"] 44 | [WhiteElo "2730"] 45 | [BlackElo "2650"] 46 | [Annotator "GM Alfonso Romero Holmes"] 47 | [PlyCount "103"] 48 | [EventDate "2001.11.27"] 49 | [Source "Mikhail Golubev"] 50 | [SourceDate "2002.01.08"] 51 | 52 | 1. d4 Nf6 2. c4 e6 3. Nf3 b6 4. g3 Ba6 5. b3 Bb4+ 6. Bd2 Be7 7. Nc3 O-O 8. Qc2 53 | (8. Bg2 8... c6 $1) 8... d5 9. cxd5 9... exd5 { } (9... Nxd5 10. Bg2 Nd7 11. 54 | O-O c5 12. Rfd1 Rc8 13. Qb2 Bb7 14. e4 $1 { 55 | Ivanchuk-Ehlvest, Tallinn rpd 2000; Topalov-Karpov, Monaco rpd 2001}) 10. Bh3 56 | $5 10... c5 11. O-O Nc6 12. Be3 Re8 13. Rfd1 h6 14. Rac1 c4 15. Ne5 Na5 16. 57 | bxc4 16... Nxc4 { } 17. Qa4 $6 (17. Bf4 $1 17... Bd6 (17... b5 18. e4 $5) 18. 58 | Qa4 Bc8 19. Bg2 Nxe5 20. dxe5 Bd7 21. Qa6 $5 (21. Qd4 Bc5 22. Qd3)) 17... Nxe3 59 | 18. fxe3 Bb7 19. Nb5 19... Bd6 { } 20. Nd7 $5 (20. Nxd6 Qxd6 21. Rf1 Re7 22. 60 | Bf5 Rc7) 20... Qe7 21. Rd3 (21. Nxf6+ Qxf6 22. Nc7 Qe7 (22... Bxc7 $5 23. Rxc7 61 | Re7) 23. Nxa8 Qxe3+ 24. Kh1 24... Bxa8 $44) (21. Nxd6 Qxd6 22. Nxf6+ Qxf6 23. 62 | Rc7 Re7 24. Bd7 $5 { } 24... Qg5 $1 25. Rf1 Bc8 26. Qc6 Rxd7 27. Rxd7 Qxe3+ 28. 63 | Rf2 Bxd7 29. Qxa8+ Kh7 30. Qxd5 Bh3) 21... Nxd7 $6 (21... Ne4 22. Nxd6 Qxd6 23. 64 | Qa3 Bc8 24. Qxd6 Nxd6 25. Rc6 Nc4 26. Rc3 Bxd7 27. Bxd7 Re7 28. Bf5 g6 29. Bd3 65 | Nxe3 30. h3 $11) 22. Nxd6 Qxd6 23. Qxd7 Qxd7 24. Bxd7 Re7 25. Bb5 { } 25... Rd8 66 | $2 (25... a6 26. Ba4 b5 27. Bb3 b4 28. Rc5 Rd8 29. Kf2 Rd6 30. Ba4 $14 (30. Rd2 67 | Rf6+ 31. Ke1 Rxe3 32. Bxd5 Bxd5 33. Rxd5 $11)) 26. Rdc3 Rd6 27. Rc7 27... Rde6 68 | { } 28. Bd7 Rd6 29. Bf5 Rxc7 30. Rxc7 g6 (30... Ba6 31. Rxa7 g6 32. Bxg6 fxg6 69 | 33. Rxa6 $18) 31. Bd3 $1 31... Bc6 32. Rxa7 Bd7 33. Kf2 Kg7 34. Ke1 Bh3 35. Kd2 70 | Rf6 36. Rb7 h5 37. a4 g5 38. e4 dxe4 39. Bxe4 $18 39... h4 40. gxh4 gxh4 41. 71 | Bf3 Kg6 42. Ke3 Bf5 43. Rc7 Re6+ 44. Kd2 Be4 45. Bxe4+ Rxe4 46. Kd3 Rg4 47. 72 | Rc6+ Kg5 48. Rxb6 Rg2 49. a5 Rxh2 50. a6 Rh3+ 51. e3 Rh2 52. Rb5+ {( 53.Ra5)} 73 | 1-0 74 | 75 | [Event "FIDE Wch KO"] 76 | [Site "Moscow"] 77 | [Date "2001.12.11"] 78 | [Round "6.3"] 79 | [White "Svidler, P."] 80 | [Black "Ponomariov, R."] 81 | [Result "0-1"] 82 | [ECO "C43"] 83 | [WhiteElo "2686"] 84 | [BlackElo "2684"] 85 | [Annotator "GM Alfonso Romero Holmes"] 86 | [PlyCount "88"] 87 | [EventDate "2001.11.27"] 88 | [Source "Mikhail Golubev"] 89 | [SourceDate "2002.01.08"] 90 | 91 | 1. e4 e5 2. Nf3 Nf6 3. d4 Nxe4 4. Bd3 d5 5. Nxe5 Nd7 6. Nxd7 Bxd7 7. O-O Bd6 8. 92 | c4 c6 9. cxd5 cxd5 10. Nc3 Nxc3 11. bxc3 O-O 12. Qh5 f5 (12... g6 13. Qxd5 Qc7 93 | 14. Qf3 $5 14... Bxh2+ 15. Kh1 Bd6 16. c4 {Rublevsky-Vallejo, Ech 2001}) 13. 94 | Re1 (13. Qf3 13... Kh8 $1 14. Bf4 Qc7 15. Bxd6 Qxd6 16. Rfe1 16... Rfe8 $11) 95 | 13... Qc7 14. Bd2 Rae8 15. Bc2 g6 16. Qf3 { } (16. Qh4) 16... Re4 $1 17. g3 ( 96 | 17. Bxe4 fxe4 18. Qe2 Bxh2+ 19. Kh1 Rf5) 17... Bb5 18. Bf4 { } (18. Bxe4 fxe4 97 | 19. Qg4 Rf5 (19... Bd7 $2 20. Qd1 Qc8 21. Qb3)) 18... Bxf4 19. gxf4 Qd6 20. 98 | Bxe4 fxe4 21. Qg3 Rxf4 22. Rab1 Bd7 23. f3 b6 (23... exf3 24. Kf2) 24. Re3 99 | 24... Qf6 { } (24... exf3 25. Kf2 Bf5 26. Re8+ Kg7 27. Rbe1 Be4 28. c4) 25. Rf1 100 | $2 (25. Rbe1 h5) (25. Rb2 25... h5 (25... exf3 26. Kf2) 26. Rf2 h4 27. Qg2 Bf5 101 | 28. h3 (28. Rf1 h3 29. Qe2 29... Qg5+ $1) 28... b5 29. fxe4 Bxe4 30. Rxf4 Qxf4 102 | 31. Qf2 Qg5+ 32. Kh2) 25... Bg4 26. Qxg4 Rxg4+ 27. fxg4 Qg5 28. Rg3 28... b5 { 103 | } 29. Rf2 Kg7 30. Kg2 a5 31. Rb2 b4 32. cxb4 axb4 33. h3 Qc1 34. Rgb3 34... Kh6 104 | $1 35. Rxb4 Qd1 36. Kf2 Kg5 37. Re2 { } 37... Kf4 $19 38. Rb3 Qxd4+ 39. Kg2 Qc4 105 | 40. Rf2+ Kg5 41. Rf7 d4 42. h4+ Kxh4 43. Rxh7+ Kxg4 44. Rg3+ Kf5 0-1 106 | 107 | [Event "FIDE Wch KO"] 108 | [Site "Moscow"] 109 | [Date "2001.12.13"] 110 | [Round "6.4"] 111 | [White "Anand, V."] 112 | [Black "Ivanchuk, V."] 113 | [Result "0-1"] 114 | [ECO "B30"] 115 | [WhiteElo "2770"] 116 | [BlackElo "2731"] 117 | [Annotator "GM Alfonso Romero Holmes"] 118 | [PlyCount "84"] 119 | [EventDate "2001.11.27"] 120 | [Source "Mikhail Golubev"] 121 | [SourceDate "2002.01.08"] 122 | 123 | 1. e4 c5 2. Nf3 Nc6 3. Nc3 e5 4. Bc4 d6 5. d3 5... Be7 { } 6. Nd2 $5 6... Nf6 124 | 7. Nf1 7... Nd7 $5 (7... Be6 8. Ne3 O-O (8... Bxc4 $6 9. dxc4 $14) 9. O-O Nd4 125 | 10. a4 a6 11. Ncd5 Nxd5 (11... b5 $2 12. axb5 axb5 13. Rxa8 Qxa8 14. Nxe7+ $18) 126 | 12. Bxd5 Rb8 13. g3 (13. c3 Nc6 14. g3 $14) 13... Bh3 14. Ng2 Qd7 (14... Ne6 127 | 15. f4 exf4 16. gxf4 Nc7 17. Ba2 d5) 15. c3 Ne6 16. f4 exf4 17. gxf4 Nc7 18. 128 | Ba2 Qg4 (18... d5 19. Qf3 Rbd8) 19. Qxg4 Bxg4 20. Rf2 Be6 21. f5 $1 21... Bxa2 129 | 22. Rxa2 b5 23. Bf4 b4 24. f6 $5 24... Bxf6 (24... gxf6 $6 25. Ne3 $14 {x f5}) 130 | 25. Bxd6 Rfc8 (25... bxc3 $2 26. e5 $1 26... cxb2 27. Rfxb2 Rxb2 28. Rxb2 $18) 131 | 26. e5 Bd8 27. e6 $6 (27. Ne3 $14) 27... Bf6 28. exf7+ Kxf7 29. Ne3 bxc3 30. 132 | bxc3 Rb1+ 31. Kg2 Ne8 32. Bg3 Kg6 33. Ng4 33... Rd8 { 133 | - Gelfand,B-Kramnik,V FIDE Sanghi Nagar (2) 1994}) 8. Nd5 (8. Ne3 Bg5) 8... 134 | Nb6 9. Nxb6 (9. Bb3 Nd4) 9... axb6 10. c3 O-O 11. Ne3 (11. Ng3 $5 11... Be6 ( 135 | 11... Bg5 12. Bxg5 Qxg5 13. O-O) 12. O-O d5 13. exd5 Bxd5 14. Qg4) 11... Bg5 136 | 12. O-O Kh8 13. Bd2 (13. Nd5 Bxc1 14. Qxc1 Ne7 15. f4 15... Be6 $1) 13... Bxe3 137 | 14. fxe3 14... Qe7 { } 15. Bd5 Be6 16. Qb3 (16. c4 f5) 16... Na5 17. Qc2 (17. 138 | Qxb6 Ra6 18. Qb5 18... Bd7 $19) 17... Qc7 (17... b5 18. c4 bxc4 19. dxc4) ( 139 | 17... f5 $6 18. Bxe6 Qxe6 19. exf5 Rxf5 20. Rxf5 Qxf5 21. Rf1 Qe6 22. c4) 18. 140 | h3 (18. c4 Nc6 19. a3 Ne7) 18... h6 19. c4 Nc6 20. Qd1 { } 20... Bxd5 $1 21. 141 | exd5 Ne7 22. a4 (22. e4 22... f5 $1 23. Qh5 Qd7) 22... f5 23. Bc3 23... Rf7 { } 142 | 24. Qb3 $2 (24. Qh5 Rf6 25. b3 $1 (25. Rf2 b5) 25... Qd7 26. Rf2) 24... Ng6 25. 143 | Rf2 Raf8 26. Raf1 { } 26... f4 $1 27. Bd2 27... f3 $1 28. Rxf3 (28. g3 Rf5 ( 144 | 28... Qd7 29. Kh2 Rf5 30. Qb5 Qc8 31. g4 R5f6) 29. e4 Rh5 30. Rxf3 (30. h4 Nxh4 145 | 31. gxh4 Qc8 32. Rh2 Qg4+ 33. Kh1 Rxh4 34. Rff2 Rxh2+ 35. Kxh2 Qh4+ 36. Kg1 146 | 36... Rf6 $19) 30... Rxf3 31. Rxf3 Rxh3 32. Kg2 Rh5 33. Be3 33... Qd7 $40) 147 | 28... Rxf3 29. gxf3 (29. Rxf3 Rxf3 30. gxf3 Qd7 31. Kh2 Qf5 32. f4 (32. Qxb6 148 | Qxf3) 32... Nh4 (32... Qh5 $1) 33. Kg3 33... g5 $1 (33... Qg6+ 34. Kxh4 Qg2 35. 149 | Qxb6 Kh7 36. Qc7 $11) 34. Bc1 $1 34... Qg6 (34... Kh7 35. Qd1) 35. fxg5 (35. 150 | fxe5 35... Qf5 $1) 35... Qxg5+ 36. Kf2 Qg2+ 37. Ke1 Nf3+ 38. Kd1 Qf1+ 39. Kc2 151 | Ne1+ 40. Kd2 Kg7 41. Qxb6 $11) 29... Qc8 30. Kh2 { } 30... Rf5 31. f4 Rh5 32. 152 | f5 (32. Rf3 Qg4 33. Rg3 Qe2+ 34. Rg2 Qf3) 32... Rxf5 33. Qd1 33... Qf8 { } 34. 153 | Rxf5 $2 (34. Kg1 Nh4 35. e4 Rxf1+ (35... Rf3 36. Bc1 Qf7 37. Rxf3 Nxf3+ 38. Kg2 154 | Nh4+ 39. Kg3 39... g5 $15) 36. Qxf1 { } 36... Qf3 $1 37. Be1 Qe3+ 38. Bf2 Qg5+ 155 | 39. Kh1 Ng6 40. Qd1 (40. Bg1 Kh7) 40... Nf4 41. Qf3 Qf6) 34... Qxf5 35. Qe2 156 | 35... e4 $1 36. Qg4 Qxg4 37. hxg4 Ne5 38. Kg3 exd3 39. b3 39... g6 $19 { } 40. 157 | e4 h5 41. gxh5 gxh5 42. Kf2 h4 0-1 158 | 159 | -------------------------------------------------------------------------------- /src/benchmark.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "misc.h" 27 | #include "position.h" 28 | #include "search.h" 29 | #include "thread.h" 30 | #include "uci.h" 31 | 32 | using namespace std; 33 | 34 | namespace { 35 | 36 | const vector Defaults = { 37 | "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 38 | "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10", 39 | "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11", 40 | "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", 41 | "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14", 42 | "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14", 43 | "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15", 44 | "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13", 45 | "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16", 46 | "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17", 47 | "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11", 48 | "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16", 49 | "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", 50 | "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", 51 | "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", 52 | "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", 53 | "6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1", 54 | "3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1", 55 | "2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1", 56 | "8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1", 57 | "7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1", 58 | "8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1", 59 | "8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1", 60 | "8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1", 61 | "8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1", 62 | "5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1", 63 | "6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1", 64 | "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1", 65 | "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1", 66 | "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1", 67 | 68 | // 5-man positions 69 | "8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate 70 | "8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate 71 | "8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw 72 | 73 | // 6-man positions 74 | "8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate 75 | "8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate 76 | "8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw 77 | 78 | // 7-man positions 79 | "8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw 80 | 81 | // Mate and stalemate positions 82 | "8/8/8/8/8/6k1/6p1/6K1 w - -", 83 | "5k2/5P2/5K2/8/8/8/8/8 b - -", 84 | "8/8/8/8/8/4k3/4p3/4K3 w - -", 85 | "8/8/8/8/8/5K2/8/3Q1k2 b - -", 86 | "7k/7P/6K1/8/3B4/8/8/8 b - -" 87 | }; 88 | 89 | } // namespace 90 | 91 | /// benchmark() runs a simple benchmark by letting Stockfish analyze a set 92 | /// of positions for a given limit each. There are five parameters: the 93 | /// transposition table size, the number of search threads that should 94 | /// be used, the limit value spent for each position (optional, default is 95 | /// depth 13), an optional file name where to look for positions in FEN 96 | /// format (defaults are the positions defined above) and the type of the 97 | /// limit value: depth (default), time in millisecs or number of nodes. 98 | 99 | void benchmark(const Position& current, istream& is) { 100 | 101 | string token; 102 | vector fens; 103 | Search::LimitsType limits; 104 | 105 | // Assign default values to missing arguments 106 | string ttSize = (is >> token) ? token : "16"; 107 | string threads = (is >> token) ? token : "1"; 108 | string limit = (is >> token) ? token : "13"; 109 | string fenFile = (is >> token) ? token : "default"; 110 | string limitType = (is >> token) ? token : "depth"; 111 | 112 | Options["Hash"] = ttSize; 113 | Options["Threads"] = threads; 114 | Search::clear(); 115 | 116 | if (limitType == "time") 117 | limits.movetime = stoi(limit); // movetime is in millisecs 118 | 119 | else if (limitType == "nodes") 120 | limits.nodes = stoll(limit); 121 | 122 | else if (limitType == "mate") 123 | limits.mate = stoi(limit); 124 | 125 | else 126 | limits.depth = stoi(limit); 127 | 128 | if (fenFile == "default") 129 | fens = Defaults; 130 | 131 | else if (fenFile == "current") 132 | fens.push_back(current.fen()); 133 | 134 | else 135 | { 136 | string fen; 137 | ifstream file(fenFile); 138 | 139 | if (!file.is_open()) 140 | { 141 | cerr << "Unable to open file " << fenFile << endl; 142 | return; 143 | } 144 | 145 | while (getline(file, fen)) 146 | if (!fen.empty()) 147 | fens.push_back(fen); 148 | 149 | file.close(); 150 | } 151 | 152 | uint64_t nodes = 0; 153 | TimePoint elapsed = now(); 154 | Position pos; 155 | 156 | for (size_t i = 0; i < fens.size(); ++i) 157 | { 158 | StateListPtr states(new std::deque(1)); 159 | pos.set(fens[i], Options["UCI_Chess960"], &states->back(), Threads.main()); 160 | 161 | cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; 162 | 163 | if (limitType == "perft") 164 | nodes += Search::perft(pos, limits.depth * ONE_PLY); 165 | 166 | else 167 | { 168 | limits.startTime = now(); 169 | Threads.start_thinking(pos, states, limits); 170 | Threads.main()->wait_for_search_finished(); 171 | nodes += Threads.nodes_searched(); 172 | } 173 | } 174 | 175 | elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' 176 | 177 | dbg_print(); // Just before exiting 178 | 179 | cerr << "\n===========================" 180 | << "\nTotal time (ms) : " << elapsed 181 | << "\nNodes searched : " << nodes 182 | << "\nNodes/second : " << 1000 * nodes / elapsed << endl; 183 | } 184 | -------------------------------------------------------------------------------- /src/bitbase.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "bitboard.h" 27 | #include "types.h" 28 | 29 | namespace { 30 | 31 | // There are 24 possible pawn squares: the first 4 files and ranks from 2 to 7 32 | const unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608 33 | 34 | // Each uint32_t stores results of 32 positions, one per bit 35 | uint32_t KPKBitbase[MAX_INDEX / 32]; 36 | 37 | // A KPK bitbase index is an integer in [0, IndexMax] range 38 | // 39 | // Information is mapped in a way that minimizes the number of iterations: 40 | // 41 | // bit 0- 5: white king square (from SQ_A1 to SQ_H8) 42 | // bit 6-11: black king square (from SQ_A1 to SQ_H8) 43 | // bit 12: side to move (WHITE or BLACK) 44 | // bit 13-14: white pawn file (from FILE_A to FILE_D) 45 | // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2) 46 | unsigned index(Color us, Square bksq, Square wksq, Square psq) { 47 | return wksq | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15); 48 | } 49 | 50 | enum Result { 51 | INVALID = 0, 52 | UNKNOWN = 1, 53 | DRAW = 2, 54 | WIN = 4 55 | }; 56 | 57 | Result& operator|=(Result& r, Result v) { return r = Result(r | v); } 58 | 59 | struct KPKPosition { 60 | KPKPosition() = default; 61 | explicit KPKPosition(unsigned idx); 62 | operator Result() const { return result; } 63 | Result classify(const std::vector& db) 64 | { return us == WHITE ? classify(db) : classify(db); } 65 | 66 | template Result classify(const std::vector& db); 67 | 68 | Color us; 69 | Square ksq[COLOR_NB], psq; 70 | Result result; 71 | }; 72 | 73 | } // namespace 74 | 75 | 76 | bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color us) { 77 | 78 | assert(file_of(wpsq) <= FILE_D); 79 | 80 | unsigned idx = index(us, bksq, wksq, wpsq); 81 | return KPKBitbase[idx / 32] & (1 << (idx & 0x1F)); 82 | } 83 | 84 | 85 | void Bitbases::init() { 86 | 87 | std::vector db(MAX_INDEX); 88 | unsigned idx, repeat = 1; 89 | 90 | // Initialize db with known win / draw positions 91 | for (idx = 0; idx < MAX_INDEX; ++idx) 92 | db[idx] = KPKPosition(idx); 93 | 94 | // Iterate through the positions until none of the unknown positions can be 95 | // changed to either wins or draws (15 cycles needed). 96 | while (repeat) 97 | for (repeat = idx = 0; idx < MAX_INDEX; ++idx) 98 | repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN); 99 | 100 | // Map 32 results into one KPKBitbase[] entry 101 | for (idx = 0; idx < MAX_INDEX; ++idx) 102 | if (db[idx] == WIN) 103 | KPKBitbase[idx / 32] |= 1 << (idx & 0x1F); 104 | } 105 | 106 | 107 | namespace { 108 | 109 | KPKPosition::KPKPosition(unsigned idx) { 110 | 111 | ksq[WHITE] = Square((idx >> 0) & 0x3F); 112 | ksq[BLACK] = Square((idx >> 6) & 0x3F); 113 | us = Color ((idx >> 12) & 0x01); 114 | psq = make_square(File((idx >> 13) & 0x3), RANK_7 - Rank((idx >> 15) & 0x7)); 115 | 116 | // Check if two pieces are on the same square or if a king can be captured 117 | if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 118 | || ksq[WHITE] == psq 119 | || ksq[BLACK] == psq 120 | || (us == WHITE && (StepAttacksBB[PAWN][psq] & ksq[BLACK]))) 121 | result = INVALID; 122 | 123 | // Immediate win if a pawn can be promoted without getting captured 124 | else if ( us == WHITE 125 | && rank_of(psq) == RANK_7 126 | && ksq[us] != psq + NORTH 127 | && ( distance(ksq[~us], psq + NORTH) > 1 128 | || (StepAttacksBB[KING][ksq[us]] & (psq + NORTH)))) 129 | result = WIN; 130 | 131 | // Immediate draw if it is a stalemate or a king captures undefended pawn 132 | else if ( us == BLACK 133 | && ( !(StepAttacksBB[KING][ksq[us]] & ~(StepAttacksBB[KING][ksq[~us]] | StepAttacksBB[PAWN][psq])) 134 | || (StepAttacksBB[KING][ksq[us]] & psq & ~StepAttacksBB[KING][ksq[~us]]))) 135 | result = DRAW; 136 | 137 | // Position will be classified later 138 | else 139 | result = UNKNOWN; 140 | } 141 | 142 | template 143 | Result KPKPosition::classify(const std::vector& db) { 144 | 145 | // White to move: If one move leads to a position classified as WIN, the result 146 | // of the current position is WIN. If all moves lead to positions classified 147 | // as DRAW, the current position is classified as DRAW, otherwise the current 148 | // position is classified as UNKNOWN. 149 | // 150 | // Black to move: If one move leads to a position classified as DRAW, the result 151 | // of the current position is DRAW. If all moves lead to positions classified 152 | // as WIN, the position is classified as WIN, otherwise the current position is 153 | // classified as UNKNOWN. 154 | 155 | const Color Them = (Us == WHITE ? BLACK : WHITE); 156 | const Result Good = (Us == WHITE ? WIN : DRAW); 157 | const Result Bad = (Us == WHITE ? DRAW : WIN); 158 | 159 | Result r = INVALID; 160 | Bitboard b = StepAttacksBB[KING][ksq[Us]]; 161 | 162 | while (b) 163 | r |= Us == WHITE ? db[index(Them, ksq[Them] , pop_lsb(&b), psq)] 164 | : db[index(Them, pop_lsb(&b), ksq[Them] , psq)]; 165 | 166 | if (Us == WHITE) 167 | { 168 | if (rank_of(psq) < RANK_7) // Single push 169 | r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH)]; 170 | 171 | if ( rank_of(psq) == RANK_2 // Double push 172 | && psq + NORTH != ksq[Us] 173 | && psq + NORTH != ksq[Them]) 174 | r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH + NORTH)]; 175 | } 176 | 177 | return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad; 178 | } 179 | 180 | } // namespace 181 | -------------------------------------------------------------------------------- /src/bitboard.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | 23 | #include "bitboard.h" 24 | #include "misc.h" 25 | 26 | uint8_t PopCnt16[1 << 16]; 27 | int SquareDistance[SQUARE_NB][SQUARE_NB]; 28 | 29 | Bitboard RookMasks [SQUARE_NB]; 30 | Bitboard RookMagics [SQUARE_NB]; 31 | Bitboard* RookAttacks[SQUARE_NB]; 32 | unsigned RookShifts [SQUARE_NB]; 33 | 34 | Bitboard BishopMasks [SQUARE_NB]; 35 | Bitboard BishopMagics [SQUARE_NB]; 36 | Bitboard* BishopAttacks[SQUARE_NB]; 37 | unsigned BishopShifts [SQUARE_NB]; 38 | 39 | Bitboard SquareBB[SQUARE_NB]; 40 | Bitboard FileBB[FILE_NB]; 41 | Bitboard RankBB[RANK_NB]; 42 | Bitboard AdjacentFilesBB[FILE_NB]; 43 | Bitboard InFrontBB[COLOR_NB][RANK_NB]; 44 | Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; 45 | Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; 46 | Bitboard LineBB[SQUARE_NB][SQUARE_NB]; 47 | Bitboard DistanceRingBB[SQUARE_NB][8]; 48 | Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; 49 | Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; 50 | Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; 51 | Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; 52 | 53 | namespace { 54 | 55 | // De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan 56 | const uint64_t DeBruijn64 = 0x3F79D71B4CB0A89ULL; 57 | const uint32_t DeBruijn32 = 0x783A9B23; 58 | 59 | int MSBTable[256]; // To implement software msb() 60 | Square BSFTable[SQUARE_NB]; // To implement software bitscan 61 | Bitboard RookTable[0x19000]; // To store rook attacks 62 | Bitboard BishopTable[0x1480]; // To store bishop attacks 63 | 64 | typedef unsigned (Fn)(Square, Bitboard); 65 | 66 | void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], 67 | Bitboard masks[], unsigned shifts[], Square deltas[], Fn index); 68 | 69 | // bsf_index() returns the index into BSFTable[] to look up the bitscan. Uses 70 | // Matt Taylor's folding for 32 bit case, extended to 64 bit by Kim Walisch. 71 | 72 | unsigned bsf_index(Bitboard b) { 73 | b ^= b - 1; 74 | return Is64Bit ? (b * DeBruijn64) >> 58 75 | : ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn32) >> 26; 76 | } 77 | 78 | 79 | // popcount16() counts the non-zero bits using SWAR-Popcount algorithm 80 | 81 | unsigned popcount16(unsigned u) { 82 | u -= (u >> 1) & 0x5555U; 83 | u = ((u >> 2) & 0x3333U) + (u & 0x3333U); 84 | u = ((u >> 4) + u) & 0x0F0FU; 85 | return (u * 0x0101U) >> 8; 86 | } 87 | } 88 | 89 | #ifdef NO_BSF 90 | 91 | /// Software fall-back of lsb() and msb() for CPU lacking hardware support 92 | 93 | Square lsb(Bitboard b) { 94 | assert(b); 95 | return BSFTable[bsf_index(b)]; 96 | } 97 | 98 | Square msb(Bitboard b) { 99 | 100 | assert(b); 101 | unsigned b32; 102 | int result = 0; 103 | 104 | if (b > 0xFFFFFFFF) 105 | { 106 | b >>= 32; 107 | result = 32; 108 | } 109 | 110 | b32 = unsigned(b); 111 | 112 | if (b32 > 0xFFFF) 113 | { 114 | b32 >>= 16; 115 | result += 16; 116 | } 117 | 118 | if (b32 > 0xFF) 119 | { 120 | b32 >>= 8; 121 | result += 8; 122 | } 123 | 124 | return Square(result + MSBTable[b32]); 125 | } 126 | 127 | #endif // ifdef NO_BSF 128 | 129 | 130 | /// Bitboards::pretty() returns an ASCII representation of a bitboard suitable 131 | /// to be printed to standard output. Useful for debugging. 132 | 133 | const std::string Bitboards::pretty(Bitboard b) { 134 | 135 | std::string s = "+---+---+---+---+---+---+---+---+\n"; 136 | 137 | for (Rank r = RANK_8; r >= RANK_1; --r) 138 | { 139 | for (File f = FILE_A; f <= FILE_H; ++f) 140 | s += b & make_square(f, r) ? "| X " : "| "; 141 | 142 | s += "|\n+---+---+---+---+---+---+---+---+\n"; 143 | } 144 | 145 | return s; 146 | } 147 | 148 | 149 | /// Bitboards::init() initializes various bitboard tables. It is called at 150 | /// startup and relies on global objects to be already zero-initialized. 151 | 152 | void Bitboards::init() { 153 | 154 | for (unsigned i = 0; i < (1 << 16); ++i) 155 | PopCnt16[i] = (uint8_t) popcount16(i); 156 | 157 | for (Square s = SQ_A1; s <= SQ_H8; ++s) 158 | { 159 | SquareBB[s] = 1ULL << s; 160 | BSFTable[bsf_index(SquareBB[s])] = s; 161 | } 162 | 163 | for (Bitboard b = 2; b < 256; ++b) 164 | MSBTable[b] = MSBTable[b - 1] + !more_than_one(b); 165 | 166 | for (File f = FILE_A; f <= FILE_H; ++f) 167 | FileBB[f] = f > FILE_A ? FileBB[f - 1] << 1 : FileABB; 168 | 169 | for (Rank r = RANK_1; r <= RANK_8; ++r) 170 | RankBB[r] = r > RANK_1 ? RankBB[r - 1] << 8 : Rank1BB; 171 | 172 | for (File f = FILE_A; f <= FILE_H; ++f) 173 | AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0); 174 | 175 | for (Rank r = RANK_1; r < RANK_8; ++r) 176 | InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]); 177 | 178 | for (Color c = WHITE; c <= BLACK; ++c) 179 | for (Square s = SQ_A1; s <= SQ_H8; ++s) 180 | { 181 | ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)]; 182 | PawnAttackSpan[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)]; 183 | PassedPawnMask[c][s] = ForwardBB[c][s] | PawnAttackSpan[c][s]; 184 | } 185 | 186 | for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) 187 | for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) 188 | if (s1 != s2) 189 | { 190 | SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); 191 | DistanceRingBB[s1][SquareDistance[s1][s2] - 1] |= s2; 192 | } 193 | 194 | int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 }, 195 | {}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } }; 196 | 197 | for (Color c = WHITE; c <= BLACK; ++c) 198 | for (PieceType pt = PAWN; pt <= KING; ++pt) 199 | for (Square s = SQ_A1; s <= SQ_H8; ++s) 200 | for (int i = 0; steps[pt][i]; ++i) 201 | { 202 | Square to = s + Square(c == WHITE ? steps[pt][i] : -steps[pt][i]); 203 | 204 | if (is_ok(to) && distance(s, to) < 3) 205 | StepAttacksBB[make_piece(c, pt)][s] |= to; 206 | } 207 | 208 | Square RookDeltas[] = { NORTH, EAST, SOUTH, WEST }; 209 | Square BishopDeltas[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; 210 | 211 | init_magics(RookTable, RookAttacks, RookMagics, RookMasks, RookShifts, RookDeltas, magic_index); 212 | init_magics(BishopTable, BishopAttacks, BishopMagics, BishopMasks, BishopShifts, BishopDeltas, magic_index); 213 | 214 | for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) 215 | { 216 | PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); 217 | PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0); 218 | 219 | for (Piece pc = W_BISHOP; pc <= W_ROOK; ++pc) 220 | for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) 221 | { 222 | if (!(PseudoAttacks[pc][s1] & s2)) 223 | continue; 224 | 225 | LineBB[s1][s2] = (attacks_bb(pc, s1, 0) & attacks_bb(pc, s2, 0)) | s1 | s2; 226 | BetweenBB[s1][s2] = attacks_bb(pc, s1, SquareBB[s2]) & attacks_bb(pc, s2, SquareBB[s1]); 227 | } 228 | } 229 | } 230 | 231 | 232 | namespace { 233 | 234 | Bitboard sliding_attack(Square deltas[], Square sq, Bitboard occupied) { 235 | 236 | Bitboard attack = 0; 237 | 238 | for (int i = 0; i < 4; ++i) 239 | for (Square s = sq + deltas[i]; 240 | is_ok(s) && distance(s, s - deltas[i]) == 1; 241 | s += deltas[i]) 242 | { 243 | attack |= s; 244 | 245 | if (occupied & s) 246 | break; 247 | } 248 | 249 | return attack; 250 | } 251 | 252 | 253 | // init_magics() computes all rook and bishop attacks at startup. Magic 254 | // bitboards are used to look up attacks of sliding pieces. As a reference see 255 | // chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we 256 | // use the so called "fancy" approach. 257 | 258 | void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], 259 | Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) { 260 | 261 | int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 }, 262 | { 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } }; 263 | 264 | Bitboard occupancy[4096], reference[4096], edges, b; 265 | int age[4096] = {0}, current = 0, i, size; 266 | 267 | // attacks[s] is a pointer to the beginning of the attacks table for square 's' 268 | attacks[SQ_A1] = table; 269 | 270 | for (Square s = SQ_A1; s <= SQ_H8; ++s) 271 | { 272 | // Board edges are not considered in the relevant occupancies 273 | edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s)); 274 | 275 | // Given a square 's', the mask is the bitboard of sliding attacks from 276 | // 's' computed on an empty board. The index must be big enough to contain 277 | // all the attacks for each possible subset of the mask and so is 2 power 278 | // the number of 1s of the mask. Hence we deduce the size of the shift to 279 | // apply to the 64 or 32 bits word to get the index. 280 | masks[s] = sliding_attack(deltas, s, 0) & ~edges; 281 | shifts[s] = (Is64Bit ? 64 : 32) - popcount(masks[s]); 282 | 283 | // Use Carry-Rippler trick to enumerate all subsets of masks[s] and 284 | // store the corresponding sliding attack bitboard in reference[]. 285 | b = size = 0; 286 | do { 287 | occupancy[size] = b; 288 | reference[size] = sliding_attack(deltas, s, b); 289 | 290 | if (HasPext) 291 | attacks[s][pext(b, masks[s])] = reference[size]; 292 | 293 | size++; 294 | b = (b - masks[s]) & masks[s]; 295 | } while (b); 296 | 297 | // Set the offset for the table of the next square. We have individual 298 | // table sizes for each square with "Fancy Magic Bitboards". 299 | if (s < SQ_H8) 300 | attacks[s + 1] = attacks[s] + size; 301 | 302 | if (HasPext) 303 | continue; 304 | 305 | PRNG rng(seeds[Is64Bit][rank_of(s)]); 306 | 307 | // Find a magic for square 's' picking up an (almost) random number 308 | // until we find the one that passes the verification test. 309 | do { 310 | do 311 | magics[s] = rng.sparse_rand(); 312 | while (popcount((magics[s] * masks[s]) >> 56) < 6); 313 | 314 | // A good magic must map every possible occupancy to an index that 315 | // looks up the correct sliding attack in the attacks[s] database. 316 | // Note that we build up the database for square 's' as a side 317 | // effect of verifying the magic. 318 | for (++current, i = 0; i < size; ++i) 319 | { 320 | unsigned idx = index(s, occupancy[i]); 321 | 322 | if (age[idx] < current) 323 | { 324 | age[idx] = current; 325 | attacks[s][idx] = reference[i]; 326 | } 327 | else if (attacks[s][idx] != reference[i]) 328 | break; 329 | } 330 | } while (i < size); 331 | } 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /src/bitboard.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef BITBOARD_H_INCLUDED 22 | #define BITBOARD_H_INCLUDED 23 | 24 | #include 25 | 26 | #include "types.h" 27 | 28 | namespace Bitbases { 29 | 30 | void init(); 31 | bool probe(Square wksq, Square wpsq, Square bksq, Color us); 32 | 33 | } 34 | 35 | namespace Bitboards { 36 | 37 | void init(); 38 | const std::string pretty(Bitboard b); 39 | 40 | } 41 | 42 | const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL; 43 | 44 | const Bitboard FileABB = 0x0101010101010101ULL; 45 | const Bitboard FileBBB = FileABB << 1; 46 | const Bitboard FileCBB = FileABB << 2; 47 | const Bitboard FileDBB = FileABB << 3; 48 | const Bitboard FileEBB = FileABB << 4; 49 | const Bitboard FileFBB = FileABB << 5; 50 | const Bitboard FileGBB = FileABB << 6; 51 | const Bitboard FileHBB = FileABB << 7; 52 | 53 | const Bitboard Rank1BB = 0xFF; 54 | const Bitboard Rank2BB = Rank1BB << (8 * 1); 55 | const Bitboard Rank3BB = Rank1BB << (8 * 2); 56 | const Bitboard Rank4BB = Rank1BB << (8 * 3); 57 | const Bitboard Rank5BB = Rank1BB << (8 * 4); 58 | const Bitboard Rank6BB = Rank1BB << (8 * 5); 59 | const Bitboard Rank7BB = Rank1BB << (8 * 6); 60 | const Bitboard Rank8BB = Rank1BB << (8 * 7); 61 | 62 | extern int SquareDistance[SQUARE_NB][SQUARE_NB]; 63 | 64 | extern Bitboard SquareBB[SQUARE_NB]; 65 | extern Bitboard FileBB[FILE_NB]; 66 | extern Bitboard RankBB[RANK_NB]; 67 | extern Bitboard AdjacentFilesBB[FILE_NB]; 68 | extern Bitboard InFrontBB[COLOR_NB][RANK_NB]; 69 | extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; 70 | extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; 71 | extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; 72 | extern Bitboard DistanceRingBB[SQUARE_NB][8]; 73 | extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; 74 | extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; 75 | extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; 76 | extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; 77 | 78 | 79 | /// Overloads of bitwise operators between a Bitboard and a Square for testing 80 | /// whether a given bit is set in a bitboard, and for setting and clearing bits. 81 | 82 | inline Bitboard operator&(Bitboard b, Square s) { 83 | return b & SquareBB[s]; 84 | } 85 | 86 | inline Bitboard operator|(Bitboard b, Square s) { 87 | return b | SquareBB[s]; 88 | } 89 | 90 | inline Bitboard operator^(Bitboard b, Square s) { 91 | return b ^ SquareBB[s]; 92 | } 93 | 94 | inline Bitboard& operator|=(Bitboard& b, Square s) { 95 | return b |= SquareBB[s]; 96 | } 97 | 98 | inline Bitboard& operator^=(Bitboard& b, Square s) { 99 | return b ^= SquareBB[s]; 100 | } 101 | 102 | inline bool more_than_one(Bitboard b) { 103 | return b & (b - 1); 104 | } 105 | 106 | 107 | /// rank_bb() and file_bb() return a bitboard representing all the squares on 108 | /// the given file or rank. 109 | 110 | inline Bitboard rank_bb(Rank r) { 111 | return RankBB[r]; 112 | } 113 | 114 | inline Bitboard rank_bb(Square s) { 115 | return RankBB[rank_of(s)]; 116 | } 117 | 118 | inline Bitboard file_bb(File f) { 119 | return FileBB[f]; 120 | } 121 | 122 | inline Bitboard file_bb(Square s) { 123 | return FileBB[file_of(s)]; 124 | } 125 | 126 | 127 | /// shift() moves a bitboard one step along direction D. Mainly for pawns 128 | 129 | template 130 | inline Bitboard shift(Bitboard b) { 131 | return D == NORTH ? b << 8 : D == SOUTH ? b >> 8 132 | : D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == SOUTH_EAST ? (b & ~FileHBB) >> 7 133 | : D == NORTH_WEST ? (b & ~FileABB) << 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9 134 | : 0; 135 | } 136 | 137 | 138 | /// adjacent_files_bb() returns a bitboard representing all the squares on the 139 | /// adjacent files of the given one. 140 | 141 | inline Bitboard adjacent_files_bb(File f) { 142 | return AdjacentFilesBB[f]; 143 | } 144 | 145 | 146 | /// between_bb() returns a bitboard representing all the squares between the two 147 | /// given ones. For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with 148 | /// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank, file 149 | /// or diagonal, 0 is returned. 150 | 151 | inline Bitboard between_bb(Square s1, Square s2) { 152 | return BetweenBB[s1][s2]; 153 | } 154 | 155 | 156 | /// in_front_bb() returns a bitboard representing all the squares on all the ranks 157 | /// in front of the given one, from the point of view of the given color. For 158 | /// instance, in_front_bb(BLACK, RANK_3) will return the squares on ranks 1 and 2. 159 | 160 | inline Bitboard in_front_bb(Color c, Rank r) { 161 | return InFrontBB[c][r]; 162 | } 163 | 164 | 165 | /// forward_bb() returns a bitboard representing all the squares along the line 166 | /// in front of the given one, from the point of view of the given color: 167 | /// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s) 168 | 169 | inline Bitboard forward_bb(Color c, Square s) { 170 | return ForwardBB[c][s]; 171 | } 172 | 173 | 174 | /// pawn_attack_span() returns a bitboard representing all the squares that can be 175 | /// attacked by a pawn of the given color when it moves along its file, starting 176 | /// from the given square: 177 | /// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s); 178 | 179 | inline Bitboard pawn_attack_span(Color c, Square s) { 180 | return PawnAttackSpan[c][s]; 181 | } 182 | 183 | 184 | /// passed_pawn_mask() returns a bitboard mask which can be used to test if a 185 | /// pawn of the given color and on the given square is a passed pawn: 186 | /// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_bb(c, s) 187 | 188 | inline Bitboard passed_pawn_mask(Color c, Square s) { 189 | return PassedPawnMask[c][s]; 190 | } 191 | 192 | 193 | /// aligned() returns true if the squares s1, s2 and s3 are aligned either on a 194 | /// straight or on a diagonal line. 195 | 196 | inline bool aligned(Square s1, Square s2, Square s3) { 197 | return LineBB[s1][s2] & s3; 198 | } 199 | 200 | 201 | /// distance() functions return the distance between x and y, defined as the 202 | /// number of steps for a king in x to reach y. Works with squares, ranks, files. 203 | 204 | template inline int distance(T x, T y) { return x < y ? y - x : x - y; } 205 | template<> inline int distance(Square x, Square y) { return SquareDistance[x][y]; } 206 | 207 | template inline int distance(T2 x, T2 y); 208 | template<> inline int distance(Square x, Square y) { return distance(file_of(x), file_of(y)); } 209 | template<> inline int distance(Square x, Square y) { return distance(rank_of(x), rank_of(y)); } 210 | 211 | 212 | /// attacks_bb() returns a bitboard representing all the squares attacked by a 213 | /// piece of type Pt (bishop or rook) placed on 's'. The helper magic_index() 214 | /// looks up the index using the 'magic bitboards' approach. 215 | template 216 | inline unsigned magic_index(Square s, Bitboard occupied) { 217 | 218 | extern Bitboard RookMasks[SQUARE_NB]; 219 | extern Bitboard RookMagics[SQUARE_NB]; 220 | extern unsigned RookShifts[SQUARE_NB]; 221 | extern Bitboard BishopMasks[SQUARE_NB]; 222 | extern Bitboard BishopMagics[SQUARE_NB]; 223 | extern unsigned BishopShifts[SQUARE_NB]; 224 | 225 | Bitboard* const Masks = Pt == ROOK ? RookMasks : BishopMasks; 226 | Bitboard* const Magics = Pt == ROOK ? RookMagics : BishopMagics; 227 | unsigned* const Shifts = Pt == ROOK ? RookShifts : BishopShifts; 228 | 229 | if (HasPext) 230 | return unsigned(pext(occupied, Masks[s])); 231 | 232 | if (Is64Bit) 233 | return unsigned(((occupied & Masks[s]) * Magics[s]) >> Shifts[s]); 234 | 235 | unsigned lo = unsigned(occupied) & unsigned(Masks[s]); 236 | unsigned hi = unsigned(occupied >> 32) & unsigned(Masks[s] >> 32); 237 | return (lo * unsigned(Magics[s]) ^ hi * unsigned(Magics[s] >> 32)) >> Shifts[s]; 238 | } 239 | 240 | template 241 | inline Bitboard attacks_bb(Square s, Bitboard occupied) { 242 | 243 | extern Bitboard* RookAttacks[SQUARE_NB]; 244 | extern Bitboard* BishopAttacks[SQUARE_NB]; 245 | 246 | return (Pt == ROOK ? RookAttacks : BishopAttacks)[s][magic_index(s, occupied)]; 247 | } 248 | 249 | inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) { 250 | 251 | switch (type_of(pc)) 252 | { 253 | case BISHOP: return attacks_bb(s, occupied); 254 | case ROOK : return attacks_bb(s, occupied); 255 | case QUEEN : return attacks_bb(s, occupied) | attacks_bb(s, occupied); 256 | default : return StepAttacksBB[pc][s]; 257 | } 258 | } 259 | 260 | 261 | /// popcount() counts the number of non-zero bits in a bitboard 262 | 263 | inline int popcount(Bitboard b) { 264 | 265 | #ifndef USE_POPCNT 266 | 267 | extern uint8_t PopCnt16[1 << 16]; 268 | union { Bitboard bb; uint16_t u[4]; } v = { b }; 269 | return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]]; 270 | 271 | #elif defined(_MSC_VER) || defined(__INTEL_COMPILER) 272 | 273 | return (int)_mm_popcnt_u64(b); 274 | 275 | #else // Assumed gcc or compatible compiler 276 | 277 | return __builtin_popcountll(b); 278 | 279 | #endif 280 | } 281 | 282 | 283 | /// lsb() and msb() return the least/most significant bit in a non-zero bitboard 284 | 285 | #if defined(__GNUC__) 286 | 287 | inline Square lsb(Bitboard b) { 288 | assert(b); 289 | return Square(__builtin_ctzll(b)); 290 | } 291 | 292 | inline Square msb(Bitboard b) { 293 | assert(b); 294 | return Square(63 ^ __builtin_clzll(b)); 295 | } 296 | 297 | #elif defined(_WIN64) && defined(_MSC_VER) 298 | 299 | inline Square lsb(Bitboard b) { 300 | assert(b); 301 | unsigned long idx; 302 | _BitScanForward64(&idx, b); 303 | return (Square) idx; 304 | } 305 | 306 | inline Square msb(Bitboard b) { 307 | assert(b); 308 | unsigned long idx; 309 | _BitScanReverse64(&idx, b); 310 | return (Square) idx; 311 | } 312 | 313 | #else 314 | 315 | #define NO_BSF // Fallback on software implementation for other cases 316 | 317 | Square lsb(Bitboard b); 318 | Square msb(Bitboard b); 319 | 320 | #endif 321 | 322 | 323 | /// pop_lsb() finds and clears the least significant bit in a non-zero bitboard 324 | 325 | inline Square pop_lsb(Bitboard* b) { 326 | const Square s = lsb(*b); 327 | *b &= *b - 1; 328 | return s; 329 | } 330 | 331 | 332 | /// frontmost_sq() and backmost_sq() return the square corresponding to the 333 | /// most/least advanced bit relative to the given color. 334 | 335 | inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); } 336 | inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); } 337 | 338 | #endif // #ifndef BITBOARD_H_INCLUDED 339 | -------------------------------------------------------------------------------- /src/endgame.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef ENDGAME_H_INCLUDED 22 | #define ENDGAME_H_INCLUDED 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "position.h" 31 | #include "types.h" 32 | 33 | 34 | /// EndgameType lists all supported endgames 35 | 36 | enum EndgameType { 37 | 38 | // Evaluation functions 39 | 40 | KNNK, // KNN vs K 41 | KXK, // Generic "mate lone king" eval 42 | KBNK, // KBN vs K 43 | KPK, // KP vs K 44 | KRKP, // KR vs KP 45 | KRKB, // KR vs KB 46 | KRKN, // KR vs KN 47 | KQKP, // KQ vs KP 48 | KQKR, // KQ vs KR 49 | 50 | 51 | // Scaling functions 52 | SCALING_FUNCTIONS, 53 | 54 | KBPsK, // KB and pawns vs K 55 | KQKRPs, // KQ vs KR and pawns 56 | KRPKR, // KRP vs KR 57 | KRPKB, // KRP vs KB 58 | KRPPKRP, // KRPP vs KRP 59 | KPsK, // K and pawns vs K 60 | KBPKB, // KBP vs KB 61 | KBPPKB, // KBPP vs KB 62 | KBPKN, // KBP vs KN 63 | KNPK, // KNP vs K 64 | KNPKB, // KNP vs KB 65 | KPKP // KP vs KP 66 | }; 67 | 68 | 69 | /// Endgame functions can be of two types depending on whether they return a 70 | /// Value or a ScaleFactor. 71 | template using 72 | eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type; 73 | 74 | 75 | /// Base and derived templates for endgame evaluation and scaling functions 76 | 77 | template 78 | struct EndgameBase { 79 | 80 | virtual ~EndgameBase() = default; 81 | virtual Color strong_side() const = 0; 82 | virtual T operator()(const Position&) const = 0; 83 | }; 84 | 85 | 86 | template> 87 | struct Endgame : public EndgameBase { 88 | 89 | explicit Endgame(Color c) : strongSide(c), weakSide(~c) {} 90 | Color strong_side() const { return strongSide; } 91 | T operator()(const Position&) const; 92 | 93 | private: 94 | Color strongSide, weakSide; 95 | }; 96 | 97 | 98 | /// The Endgames class stores the pointers to endgame evaluation and scaling 99 | /// base objects in two std::map. We use polymorphism to invoke the actual 100 | /// endgame function by calling its virtual operator(). 101 | 102 | class Endgames { 103 | 104 | template using Map = std::map>>; 105 | 106 | template> 107 | void add(const std::string& code); 108 | 109 | template 110 | Map& map() { 111 | return std::get::value>(maps); 112 | } 113 | 114 | std::pair, Map> maps; 115 | 116 | public: 117 | Endgames(); 118 | 119 | template 120 | EndgameBase* probe(Key key) { 121 | return map().count(key) ? map()[key].get() : nullptr; 122 | } 123 | }; 124 | 125 | #endif // #ifndef ENDGAME_H_INCLUDED 126 | -------------------------------------------------------------------------------- /src/evaluate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef EVALUATE_H_INCLUDED 22 | #define EVALUATE_H_INCLUDED 23 | 24 | #include 25 | 26 | #include "types.h" 27 | 28 | class Position; 29 | 30 | namespace Eval { 31 | 32 | const Value Tempo = Value(20); // Must be visible to search 33 | 34 | std::string trace(const Position& pos); 35 | 36 | template 37 | Value evaluate(const Position& pos); 38 | } 39 | 40 | #endif // #ifndef EVALUATE_H_INCLUDED 41 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | 23 | #include "bitboard.h" 24 | #include "position.h" 25 | #include "search.h" 26 | #include "thread.h" 27 | #include "tt.h" 28 | #include "uci.h" 29 | #include "syzygy/tbprobe.h" 30 | 31 | namespace PSQT { 32 | void init(); 33 | } 34 | 35 | namespace Parser { 36 | void init(); 37 | } 38 | 39 | int main(int argc, char* argv[]) { 40 | 41 | UCI::init(Options); 42 | PSQT::init(); 43 | Bitboards::init(); 44 | Position::init(); 45 | Bitbases::init(); 46 | Search::init(); 47 | Pawns::init(); 48 | Threads.init(); 49 | Tablebases::init(Options["SyzygyPath"]); 50 | TT.resize(Options["Hash"]); 51 | Parser::init(); 52 | 53 | UCI::loop(argc, argv); 54 | 55 | Threads.exit(); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /src/material.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include // For std::min 22 | #include 23 | #include // For std::memset 24 | 25 | #include "material.h" 26 | #include "thread.h" 27 | 28 | using namespace std; 29 | 30 | namespace { 31 | 32 | // Polynomial material imbalance parameters 33 | 34 | const int QuadraticOurs[][PIECE_TYPE_NB] = { 35 | // OUR PIECES 36 | // pair pawn knight bishop rook queen 37 | {1667 }, // Bishop pair 38 | { 40, 2 }, // Pawn 39 | { 32, 255, -3 }, // Knight OUR PIECES 40 | { 0, 104, 4, 0 }, // Bishop 41 | { -26, -2, 47, 105, -149 }, // Rook 42 | {-185, 24, 122, 137, -134, 0 } // Queen 43 | }; 44 | 45 | const int QuadraticTheirs[][PIECE_TYPE_NB] = { 46 | // THEIR PIECES 47 | // pair pawn knight bishop rook queen 48 | { 0 }, // Bishop pair 49 | { 36, 0 }, // Pawn 50 | { 9, 63, 0 }, // Knight OUR PIECES 51 | { 59, 65, 42, 0 }, // Bishop 52 | { 46, 39, 24, -24, 0 }, // Rook 53 | { 101, 100, -37, 141, 268, 0 } // Queen 54 | }; 55 | 56 | // Endgame evaluation and scaling functions are accessed directly and not through 57 | // the function maps because they correspond to more than one material hash key. 58 | Endgame EvaluateKXK[] = { Endgame(WHITE), Endgame(BLACK) }; 59 | 60 | Endgame ScaleKBPsK[] = { Endgame(WHITE), Endgame(BLACK) }; 61 | Endgame ScaleKQKRPs[] = { Endgame(WHITE), Endgame(BLACK) }; 62 | Endgame ScaleKPsK[] = { Endgame(WHITE), Endgame(BLACK) }; 63 | Endgame ScaleKPKP[] = { Endgame(WHITE), Endgame(BLACK) }; 64 | 65 | // Helper used to detect a given material distribution 66 | bool is_KXK(const Position& pos, Color us) { 67 | return !more_than_one(pos.pieces(~us)) 68 | && pos.non_pawn_material(us) >= RookValueMg; 69 | } 70 | 71 | bool is_KBPsKs(const Position& pos, Color us) { 72 | return pos.non_pawn_material(us) == BishopValueMg 73 | && pos.count(us) == 1 74 | && pos.count(us) >= 1; 75 | } 76 | 77 | bool is_KQKRPs(const Position& pos, Color us) { 78 | return !pos.count(us) 79 | && pos.non_pawn_material(us) == QueenValueMg 80 | && pos.count(us) == 1 81 | && pos.count(~us) == 1 82 | && pos.count(~us) >= 1; 83 | } 84 | 85 | /// imbalance() calculates the imbalance by comparing the piece count of each 86 | /// piece type for both colors. 87 | template 88 | int imbalance(const int pieceCount[][PIECE_TYPE_NB]) { 89 | 90 | const Color Them = (Us == WHITE ? BLACK : WHITE); 91 | 92 | int bonus = 0; 93 | 94 | // Second-degree polynomial material imbalance by Tord Romstad 95 | for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1) 96 | { 97 | if (!pieceCount[Us][pt1]) 98 | continue; 99 | 100 | int v = 0; 101 | 102 | for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2) 103 | v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2] 104 | + QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2]; 105 | 106 | bonus += pieceCount[Us][pt1] * v; 107 | } 108 | 109 | return bonus; 110 | } 111 | 112 | } // namespace 113 | 114 | namespace Material { 115 | 116 | /// Material::probe() looks up the current position's material configuration in 117 | /// the material hash table. It returns a pointer to the Entry if the position 118 | /// is found. Otherwise a new Entry is computed and stored there, so we don't 119 | /// have to recompute all when the same material configuration occurs again. 120 | 121 | Entry* probe(const Position& pos) { 122 | 123 | Key key = pos.material_key(); 124 | Entry* e = pos.this_thread()->materialTable[key]; 125 | 126 | if (e->key == key) 127 | return e; 128 | 129 | std::memset(e, 0, sizeof(Entry)); 130 | e->key = key; 131 | e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; 132 | e->gamePhase = pos.game_phase(); 133 | 134 | // Let's look if we have a specialized evaluation function for this particular 135 | // material configuration. Firstly we look for a fixed configuration one, then 136 | // for a generic one if the previous search failed. 137 | if ((e->evaluationFunction = pos.this_thread()->endgames.probe(key)) != nullptr) 138 | return e; 139 | 140 | for (Color c = WHITE; c <= BLACK; ++c) 141 | if (is_KXK(pos, c)) 142 | { 143 | e->evaluationFunction = &EvaluateKXK[c]; 144 | return e; 145 | } 146 | 147 | // OK, we didn't find any special evaluation function for the current material 148 | // configuration. Is there a suitable specialized scaling function? 149 | EndgameBase* sf; 150 | 151 | if ((sf = pos.this_thread()->endgames.probe(key)) != nullptr) 152 | { 153 | e->scalingFunction[sf->strong_side()] = sf; // Only strong color assigned 154 | return e; 155 | } 156 | 157 | // We didn't find any specialized scaling function, so fall back on generic 158 | // ones that refer to more than one material distribution. Note that in this 159 | // case we don't return after setting the function. 160 | for (Color c = WHITE; c <= BLACK; ++c) 161 | { 162 | if (is_KBPsKs(pos, c)) 163 | e->scalingFunction[c] = &ScaleKBPsK[c]; 164 | 165 | else if (is_KQKRPs(pos, c)) 166 | e->scalingFunction[c] = &ScaleKQKRPs[c]; 167 | } 168 | 169 | Value npm_w = pos.non_pawn_material(WHITE); 170 | Value npm_b = pos.non_pawn_material(BLACK); 171 | 172 | if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board 173 | { 174 | if (!pos.count(BLACK)) 175 | { 176 | assert(pos.count(WHITE) >= 2); 177 | 178 | e->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; 179 | } 180 | else if (!pos.count(WHITE)) 181 | { 182 | assert(pos.count(BLACK) >= 2); 183 | 184 | e->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; 185 | } 186 | else if (pos.count(WHITE) == 1 && pos.count(BLACK) == 1) 187 | { 188 | // This is a special case because we set scaling functions 189 | // for both colors instead of only one. 190 | e->scalingFunction[WHITE] = &ScaleKPKP[WHITE]; 191 | e->scalingFunction[BLACK] = &ScaleKPKP[BLACK]; 192 | } 193 | } 194 | 195 | // Zero or just one pawn makes it difficult to win, even with a small material 196 | // advantage. This catches some trivial draws like KK, KBK and KNK and gives a 197 | // drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN). 198 | if (!pos.count(WHITE) && npm_w - npm_b <= BishopValueMg) 199 | e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW : 200 | npm_b <= BishopValueMg ? 4 : 14); 201 | 202 | if (!pos.count(BLACK) && npm_b - npm_w <= BishopValueMg) 203 | e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW : 204 | npm_w <= BishopValueMg ? 4 : 14); 205 | 206 | if (pos.count(WHITE) == 1 && npm_w - npm_b <= BishopValueMg) 207 | e->factor[WHITE] = (uint8_t) SCALE_FACTOR_ONEPAWN; 208 | 209 | if (pos.count(BLACK) == 1 && npm_b - npm_w <= BishopValueMg) 210 | e->factor[BLACK] = (uint8_t) SCALE_FACTOR_ONEPAWN; 211 | 212 | // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder 213 | // for the bishop pair "extended piece", which allows us to be more flexible 214 | // in defining bishop pair bonuses. 215 | const int PieceCount[COLOR_NB][PIECE_TYPE_NB] = { 216 | { pos.count(WHITE) > 1, pos.count(WHITE), pos.count(WHITE), 217 | pos.count(WHITE) , pos.count(WHITE), pos.count(WHITE) }, 218 | { pos.count(BLACK) > 1, pos.count(BLACK), pos.count(BLACK), 219 | pos.count(BLACK) , pos.count(BLACK), pos.count(BLACK) } }; 220 | 221 | e->value = int16_t((imbalance(PieceCount) - imbalance(PieceCount)) / 16); 222 | return e; 223 | } 224 | 225 | } // namespace Material 226 | -------------------------------------------------------------------------------- /src/material.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef MATERIAL_H_INCLUDED 22 | #define MATERIAL_H_INCLUDED 23 | 24 | #include "endgame.h" 25 | #include "misc.h" 26 | #include "position.h" 27 | #include "types.h" 28 | 29 | namespace Material { 30 | 31 | /// Material::Entry contains various information about a material configuration. 32 | /// It contains a material imbalance evaluation, a function pointer to a special 33 | /// endgame evaluation function (which in most cases is NULL, meaning that the 34 | /// standard evaluation function will be used), and scale factors. 35 | /// 36 | /// The scale factors are used to scale the evaluation score up or down. For 37 | /// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4, 38 | /// which will result in scores of absolute value less than one pawn. 39 | 40 | struct Entry { 41 | 42 | Score imbalance() const { return make_score(value, value); } 43 | Phase game_phase() const { return gamePhase; } 44 | bool specialized_eval_exists() const { return evaluationFunction != nullptr; } 45 | Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); } 46 | 47 | // scale_factor takes a position and a color as input and returns a scale factor 48 | // for the given color. We have to provide the position in addition to the color 49 | // because the scale factor may also be a function which should be applied to 50 | // the position. For instance, in KBP vs K endgames, the scaling function looks 51 | // for rook pawns and wrong-colored bishops. 52 | ScaleFactor scale_factor(const Position& pos, Color c) const { 53 | ScaleFactor sf = scalingFunction[c] ? (*scalingFunction[c])(pos) 54 | : SCALE_FACTOR_NONE; 55 | return sf != SCALE_FACTOR_NONE ? sf : ScaleFactor(factor[c]); 56 | } 57 | 58 | Key key; 59 | int16_t value; 60 | uint8_t factor[COLOR_NB]; 61 | EndgameBase* evaluationFunction; 62 | EndgameBase* scalingFunction[COLOR_NB]; // Could be one for each 63 | // side (e.g. KPKP, KBPsKs) 64 | Phase gamePhase; 65 | }; 66 | 67 | typedef HashTable Table; 68 | 69 | Entry* probe(const Position& pos); 70 | 71 | } // namespace Material 72 | 73 | #endif // #ifndef MATERIAL_H_INCLUDED 74 | -------------------------------------------------------------------------------- /src/misc.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifdef _WIN32 22 | #if _WIN32_WINNT < 0x0601 23 | #undef _WIN32_WINNT 24 | #define _WIN32_WINNT 0x0601 // Force to include needed API prototypes 25 | #endif 26 | #define WIN32_LEAN_AND_MEAN 27 | #ifndef NOMINMAX 28 | #define NOMINMAX 29 | #endif 30 | #include 31 | // The needed Windows API for processor groups could be missed from old Windows 32 | // versions, so instead of calling them directly (forcing the linker to resolve 33 | // the calls at compile time), try to load them at runtime. To do this we need 34 | // first to define the corresponding function pointers. 35 | extern "C" { 36 | typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP, 37 | PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD); 38 | typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY); 39 | typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); 40 | } 41 | #endif 42 | 43 | #ifndef _WIN32 44 | #include 45 | #include 46 | #include 47 | #include 48 | #endif 49 | 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | #include "misc.h" 57 | #include "thread.h" 58 | 59 | using namespace std; 60 | 61 | namespace { 62 | 63 | /// Version number. If Version is left empty, then compile date in the format 64 | /// DD-MM-YY and show in engine_info. 65 | const string Version = ""; 66 | 67 | /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and 68 | /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We 69 | /// can toggle the logging of std::cout and std:cin at runtime whilst preserving 70 | /// usual I/O functionality, all without changing a single line of code! 71 | /// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81 72 | 73 | struct Tie: public streambuf { // MSVC requires split streambuf for cin and cout 74 | 75 | Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {} 76 | 77 | int sync() { return logBuf->pubsync(), buf->pubsync(); } 78 | int overflow(int c) { return log(buf->sputc((char)c), "<< "); } 79 | int underflow() { return buf->sgetc(); } 80 | int uflow() { return log(buf->sbumpc(), ">> "); } 81 | 82 | streambuf *buf, *logBuf; 83 | 84 | int log(int c, const char* prefix) { 85 | 86 | static int last = '\n'; // Single log file 87 | 88 | if (last == '\n') 89 | logBuf->sputn(prefix, 3); 90 | 91 | return last = logBuf->sputc((char)c); 92 | } 93 | }; 94 | 95 | class Logger { 96 | 97 | Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf()) {} 98 | ~Logger() { start(""); } 99 | 100 | ofstream file; 101 | Tie in, out; 102 | 103 | public: 104 | static void start(const std::string& fname) { 105 | 106 | static Logger l; 107 | 108 | if (!fname.empty() && !l.file.is_open()) 109 | { 110 | l.file.open(fname, ifstream::out); 111 | cin.rdbuf(&l.in); 112 | cout.rdbuf(&l.out); 113 | } 114 | else if (fname.empty() && l.file.is_open()) 115 | { 116 | cout.rdbuf(l.out.buf); 117 | cin.rdbuf(l.in.buf); 118 | l.file.close(); 119 | } 120 | } 121 | }; 122 | 123 | } // namespace 124 | 125 | /// engine_info() returns the full name of the current Stockfish version. This 126 | /// will be either "Stockfish DD-MM-YY" (where DD-MM-YY is the date when 127 | /// the program was compiled) or "Stockfish ", depending on whether 128 | /// Version is empty. 129 | 130 | const string engine_info(bool to_uci) { 131 | 132 | const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); 133 | string month, day, year; 134 | stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008" 135 | 136 | ss << "Stockfish " << Version << setfill('0'); 137 | 138 | if (Version.empty()) 139 | { 140 | date >> month >> day >> year; 141 | ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2); 142 | } 143 | 144 | ss << (Is64Bit ? " 64" : "") 145 | << (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : "")) 146 | << (to_uci ? "\nid author ": " by ") 147 | << "T. Romstad, M. Costalba, J. Kiiski, G. Linscott"; 148 | 149 | return ss.str(); 150 | } 151 | 152 | 153 | /// Debug functions used mainly to collect run-time statistics 154 | static int64_t hits[2], means[2]; 155 | 156 | void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } 157 | void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); } 158 | void dbg_mean_of(int v) { ++means[0]; means[1] += v; } 159 | 160 | void dbg_print() { 161 | 162 | if (hits[0]) 163 | cerr << "Total " << hits[0] << " Hits " << hits[1] 164 | << " hit rate (%) " << 100 * hits[1] / hits[0] << endl; 165 | 166 | if (means[0]) 167 | cerr << "Total " << means[0] << " Mean " 168 | << (double)means[1] / means[0] << endl; 169 | } 170 | 171 | 172 | /// Used to serialize access to std::cout to avoid multiple threads writing at 173 | /// the same time. 174 | 175 | std::ostream& operator<<(std::ostream& os, SyncCout sc) { 176 | 177 | static Mutex m; 178 | 179 | if (sc == IO_LOCK) 180 | m.lock(); 181 | 182 | if (sc == IO_UNLOCK) 183 | m.unlock(); 184 | 185 | return os; 186 | } 187 | 188 | 189 | /// Trampoline helper to avoid moving Logger to misc.h 190 | void start_logger(const std::string& fname) { Logger::start(fname); } 191 | 192 | 193 | /// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking 194 | /// function that doesn't stall the CPU waiting for data to be loaded from memory, 195 | /// which can be quite slow. 196 | #ifdef NO_PREFETCH 197 | 198 | void prefetch(void*) {} 199 | 200 | #else 201 | 202 | void prefetch(void* addr) { 203 | 204 | # if defined(__INTEL_COMPILER) 205 | // This hack prevents prefetches from being optimized away by 206 | // Intel compiler. Both MSVC and gcc seem not be affected by this. 207 | __asm__ (""); 208 | # endif 209 | 210 | # if defined(__INTEL_COMPILER) || defined(_MSC_VER) 211 | _mm_prefetch((char*)addr, _MM_HINT_T0); 212 | # else 213 | __builtin_prefetch(addr); 214 | # endif 215 | } 216 | 217 | #endif 218 | 219 | namespace WinProcGroup { 220 | 221 | #ifndef _WIN32 222 | 223 | void bindThisThread(size_t) {} 224 | 225 | #else 226 | 227 | /// get_group() retrieves logical processor information using Windows specific 228 | /// API and returns the best group id for the thread with index idx. Original 229 | /// code from Texel by Peter Österlund. 230 | 231 | int get_group(size_t idx) { 232 | 233 | int threads = 0; 234 | int nodes = 0; 235 | int cores = 0; 236 | DWORD returnLength = 0; 237 | DWORD byteOffset = 0; 238 | 239 | // Early exit if the needed API is not available at runtime 240 | HMODULE k32 = GetModuleHandle("Kernel32.dll"); 241 | auto fun1 = (fun1_t)GetProcAddress(k32, "GetLogicalProcessorInformationEx"); 242 | if (!fun1) 243 | return -1; 244 | 245 | // First call to get returnLength. We expect it to fail due to null buffer 246 | if (fun1(RelationAll, nullptr, &returnLength)) 247 | return -1; 248 | 249 | // Once we know returnLength, allocate the buffer 250 | SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr; 251 | ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength); 252 | 253 | // Second call, now we expect to succeed 254 | if (!fun1(RelationAll, buffer, &returnLength)) 255 | { 256 | free(buffer); 257 | return -1; 258 | } 259 | 260 | while (ptr->Size > 0 && byteOffset + ptr->Size <= returnLength) 261 | { 262 | if (ptr->Relationship == RelationNumaNode) 263 | nodes++; 264 | 265 | else if (ptr->Relationship == RelationProcessorCore) 266 | { 267 | cores++; 268 | threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1; 269 | } 270 | 271 | byteOffset += ptr->Size; 272 | ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size); 273 | } 274 | 275 | free(buffer); 276 | 277 | std::vector groups; 278 | 279 | // Run as many threads as possible on the same node until core limit is 280 | // reached, then move on filling the next node. 281 | for (int n = 0; n < nodes; n++) 282 | for (int i = 0; i < cores / nodes; i++) 283 | groups.push_back(n); 284 | 285 | // In case a core has more than one logical processor (we assume 2) and we 286 | // have still threads to allocate, then spread them evenly across available 287 | // nodes. 288 | for (int t = 0; t < threads - cores; t++) 289 | groups.push_back(t % nodes); 290 | 291 | // If we still have more threads than the total number of logical processors 292 | // then return -1 and let the OS to decide what to do. 293 | return idx < groups.size() ? groups[idx] : -1; 294 | } 295 | 296 | 297 | /// bindThisThread() set the group affinity of the current thread 298 | 299 | void bindThisThread(size_t idx) { 300 | 301 | // If OS already scheduled us on a different group than 0 then don't overwrite 302 | // the choice, eventually we are one of many one-threaded processes running on 303 | // some Windows NUMA hardware, for instance in fishtest. To make it simple, 304 | // just check if running threads are below a threshold, in this case all this 305 | // NUMA machinery is not needed. 306 | if (Threads.size() < 8) 307 | return; 308 | 309 | // Use only local variables to be thread-safe 310 | int group = get_group(idx); 311 | 312 | if (group == -1) 313 | return; 314 | 315 | // Early exit if the needed API are not available at runtime 316 | HMODULE k32 = GetModuleHandle("Kernel32.dll"); 317 | auto fun2 = (fun2_t)GetProcAddress(k32, "GetNumaNodeProcessorMaskEx"); 318 | auto fun3 = (fun3_t)GetProcAddress(k32, "SetThreadGroupAffinity"); 319 | 320 | if (!fun2 || !fun3) 321 | return; 322 | 323 | GROUP_AFFINITY affinity; 324 | if (fun2(group, &affinity)) 325 | fun3(GetCurrentThread(), &affinity, nullptr); 326 | } 327 | 328 | #endif 329 | 330 | } // namespace WinProcGroup 331 | 332 | void mem_map(const char* fname, void** baseAddress, uint64_t* mapping, uint64_t* size) { 333 | 334 | #ifndef _WIN32 335 | struct stat statbuf; 336 | int fd = ::open(fname, O_RDONLY); 337 | fstat(fd, &statbuf); 338 | *mapping = *size = statbuf.st_size; 339 | *baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); 340 | ::close(fd); 341 | if (*baseAddress == MAP_FAILED) 342 | { 343 | std::cerr << "Could not mmap() " << fname << std::endl; 344 | exit(1); 345 | } 346 | #else 347 | HANDLE fd = CreateFile(fname, GENERIC_READ, FILE_SHARE_READ, nullptr, 348 | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); 349 | DWORD size_high; 350 | DWORD size_low = GetFileSize(fd, &size_high); 351 | HANDLE mmap = CreateFileMapping(fd, nullptr, PAGE_READONLY, size_high, size_low, nullptr); 352 | CloseHandle(fd); 353 | if (!mmap) 354 | { 355 | std::cerr << "CreateFileMapping() failed" << std::endl; 356 | exit(1); 357 | } 358 | *size = ((size_t)size_high << 32) | (size_t)size_low; 359 | *mapping = (uint64_t)mmap; 360 | *baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0); 361 | if (!*baseAddress) 362 | { 363 | std::cerr << "MapViewOfFile() failed, name = " << fname 364 | << ", error = " << GetLastError() << std::endl; 365 | exit(1); 366 | } 367 | #endif 368 | } 369 | 370 | void mem_unmap(void* baseAddress, uint64_t mapping) { 371 | 372 | #ifndef _WIN32 373 | munmap(baseAddress, mapping); 374 | #else 375 | UnmapViewOfFile(baseAddress); 376 | CloseHandle((HANDLE)mapping); 377 | #endif 378 | } 379 | 380 | -------------------------------------------------------------------------------- /src/misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef MISC_H_INCLUDED 22 | #define MISC_H_INCLUDED 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "types.h" 31 | 32 | const std::string engine_info(bool to_uci = false); 33 | void prefetch(void* addr); 34 | void start_logger(const std::string& fname); 35 | 36 | void dbg_hit_on(bool b); 37 | void dbg_hit_on(bool c, bool b); 38 | void dbg_mean_of(int v); 39 | void dbg_print(); 40 | 41 | typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds 42 | 43 | inline TimePoint now() { 44 | return std::chrono::duration_cast 45 | (std::chrono::steady_clock::now().time_since_epoch()).count(); 46 | } 47 | 48 | template 49 | struct HashTable { 50 | Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; } 51 | 52 | private: 53 | std::vector table = std::vector(Size); 54 | }; 55 | 56 | 57 | enum SyncCout { IO_LOCK, IO_UNLOCK }; 58 | std::ostream& operator<<(std::ostream&, SyncCout); 59 | 60 | #define sync_cout std::cout << IO_LOCK 61 | #define sync_endl std::endl << IO_UNLOCK 62 | 63 | 64 | /// xorshift64star Pseudo-Random Number Generator 65 | /// This class is based on original code written and dedicated 66 | /// to the public domain by Sebastiano Vigna (2014). 67 | /// It has the following characteristics: 68 | /// 69 | /// - Outputs 64-bit numbers 70 | /// - Passes Dieharder and SmallCrush test batteries 71 | /// - Does not require warm-up, no zeroland to escape 72 | /// - Internal state is a single 64-bit integer 73 | /// - Period is 2^64 - 1 74 | /// - Speed: 1.60 ns/call (Core i7 @3.40GHz) 75 | /// 76 | /// For further analysis see 77 | /// 78 | 79 | class PRNG { 80 | 81 | uint64_t s; 82 | 83 | uint64_t rand64() { 84 | 85 | s ^= s >> 12, s ^= s << 25, s ^= s >> 27; 86 | return s * 2685821657736338717LL; 87 | } 88 | 89 | public: 90 | PRNG(uint64_t seed) : s(seed) { assert(seed); } 91 | 92 | template T rand() { return T(rand64()); } 93 | 94 | /// Special generator used to fast init magic numbers. 95 | /// Output values only have 1/8th of their bits set on average. 96 | template T sparse_rand() 97 | { return T(rand64() & rand64() & rand64()); } 98 | }; 99 | 100 | 101 | /// Under Windows it is not possible for a process to run on more than one 102 | /// logical processor group. This usually means to be limited to use max 64 103 | /// cores. To overcome this, some special platform specific API should be 104 | /// called to set group affinity for each thread. Original code from Texel by 105 | /// Peter Österlund. 106 | 107 | namespace WinProcGroup { 108 | void bindThisThread(size_t idx); 109 | } 110 | 111 | void mem_map(const char* fname, void** baseAddress, uint64_t* mapping, uint64_t* size); 112 | void mem_unmap(void* baseAddress, uint64_t mapping); 113 | 114 | 115 | /// Convert a number of type T into a sequence of bytes in big-endian format 116 | 117 | template uint8_t* write_be(const T& n, uint8_t* data) { 118 | 119 | for (int i = 8 * (sizeof(T) - 1); i >= 0; i -= 8) 120 | *data++ = uint8_t(n >> i); 121 | 122 | return data; 123 | } 124 | 125 | template uint8_t* read_be(T& n, uint8_t* data) { 126 | 127 | n = 0; 128 | for (int i = sizeof(T); i > 0; --i) 129 | n = (n << 8) + *data++; 130 | 131 | return data; 132 | } 133 | 134 | #endif // #ifndef MISC_H_INCLUDED 135 | -------------------------------------------------------------------------------- /src/movepick.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | 23 | #include "movepick.h" 24 | #include "thread.h" 25 | 26 | namespace { 27 | 28 | enum Stages { 29 | MAIN_SEARCH, CAPTURES_INIT, GOOD_CAPTURES, KILLERS, COUNTERMOVE, QUIET_INIT, QUIET, BAD_CAPTURES, 30 | EVASION, EVASIONS_INIT, ALL_EVASIONS, 31 | PROBCUT, PROBCUT_INIT, PROBCUT_CAPTURES, 32 | QSEARCH_WITH_CHECKS, QCAPTURES_1_INIT, QCAPTURES_1, QCHECKS, 33 | QSEARCH_NO_CHECKS, QCAPTURES_2_INIT, QCAPTURES_2, 34 | QSEARCH_RECAPTURES, QRECAPTURES 35 | }; 36 | 37 | // Our insertion sort, which is guaranteed to be stable, as it should be 38 | void insertion_sort(ExtMove* begin, ExtMove* end) 39 | { 40 | ExtMove tmp, *p, *q; 41 | 42 | for (p = begin + 1; p < end; ++p) 43 | { 44 | tmp = *p; 45 | for (q = p; q != begin && *(q-1) < tmp; --q) 46 | *q = *(q-1); 47 | *q = tmp; 48 | } 49 | } 50 | 51 | // pick_best() finds the best move in the range (begin, end) and moves it to 52 | // the front. It's faster than sorting all the moves in advance when there 53 | // are few moves, e.g., the possible captures. 54 | Move pick_best(ExtMove* begin, ExtMove* end) 55 | { 56 | std::swap(*begin, *std::max_element(begin, end)); 57 | return *begin; 58 | } 59 | 60 | } // namespace 61 | 62 | 63 | /// Constructors of the MovePicker class. As arguments we pass information 64 | /// to help it to return the (presumably) good moves first, to decide which 65 | /// moves to return (in the quiescence search, for instance, we only want to 66 | /// search captures, promotions, and some checks) and how important good move 67 | /// ordering is at the current node. 68 | 69 | MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Search::Stack* s) 70 | : pos(p), ss(s), depth(d) { 71 | 72 | assert(d > DEPTH_ZERO); 73 | 74 | Square prevSq = to_sq((ss-1)->currentMove); 75 | countermove = pos.this_thread()->counterMoves[pos.piece_on(prevSq)][prevSq]; 76 | 77 | stage = pos.checkers() ? EVASION : MAIN_SEARCH; 78 | ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; 79 | stage += (ttMove == MOVE_NONE); 80 | } 81 | 82 | MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Square s) 83 | : pos(p) { 84 | 85 | assert(d <= DEPTH_ZERO); 86 | 87 | if (pos.checkers()) 88 | stage = EVASION; 89 | 90 | else if (d > DEPTH_QS_NO_CHECKS) 91 | stage = QSEARCH_WITH_CHECKS; 92 | 93 | else if (d > DEPTH_QS_RECAPTURES) 94 | stage = QSEARCH_NO_CHECKS; 95 | 96 | else 97 | { 98 | stage = QSEARCH_RECAPTURES; 99 | recaptureSquare = s; 100 | return; 101 | } 102 | 103 | ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; 104 | stage += (ttMove == MOVE_NONE); 105 | } 106 | 107 | MovePicker::MovePicker(const Position& p, Move ttm, Value th) 108 | : pos(p), threshold(th) { 109 | 110 | assert(!pos.checkers()); 111 | 112 | stage = PROBCUT; 113 | 114 | // In ProbCut we generate captures with SEE higher than the given threshold 115 | ttMove = ttm 116 | && pos.pseudo_legal(ttm) 117 | && pos.capture(ttm) 118 | && pos.see_ge(ttm, threshold + 1)? ttm : MOVE_NONE; 119 | 120 | stage += (ttMove == MOVE_NONE); 121 | } 122 | 123 | 124 | /// score() assigns a numerical value to each move in a move list. The moves with 125 | /// highest values will be picked first. 126 | template<> 127 | void MovePicker::score() { 128 | // Winning and equal captures in the main search are ordered by MVV, preferring 129 | // captures near our home rank. Surprisingly, this appears to perform slightly 130 | // better than SEE-based move ordering: exchanging big pieces before capturing 131 | // a hanging piece probably helps to reduce the subtree size. 132 | // In the main search we want to push captures with negative SEE values to the 133 | // badCaptures[] array, but instead of doing it now we delay until the move 134 | // has been picked up, saving some SEE calls in case we get a cutoff. 135 | for (auto& m : *this) 136 | m.value = PieceValue[MG][pos.piece_on(to_sq(m))] 137 | - Value(200 * relative_rank(pos.side_to_move(), to_sq(m))); 138 | } 139 | 140 | template<> 141 | void MovePicker::score() { 142 | 143 | const HistoryStats& history = pos.this_thread()->history; 144 | const FromToStats& fromTo = pos.this_thread()->fromTo; 145 | 146 | const CounterMoveStats* cm = (ss-1)->counterMoves; 147 | const CounterMoveStats* fm = (ss-2)->counterMoves; 148 | const CounterMoveStats* f2 = (ss-4)->counterMoves; 149 | 150 | Color c = pos.side_to_move(); 151 | 152 | for (auto& m : *this) 153 | m.value = history[pos.moved_piece(m)][to_sq(m)] 154 | + (cm ? (*cm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) 155 | + (fm ? (*fm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) 156 | + (f2 ? (*f2)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) 157 | + fromTo.get(c, m); 158 | } 159 | 160 | template<> 161 | void MovePicker::score() { 162 | // Try captures ordered by MVV/LVA, then non-captures ordered by history value 163 | const HistoryStats& history = pos.this_thread()->history; 164 | const FromToStats& fromTo = pos.this_thread()->fromTo; 165 | Color c = pos.side_to_move(); 166 | 167 | for (auto& m : *this) 168 | if (pos.capture(m)) 169 | m.value = PieceValue[MG][pos.piece_on(to_sq(m))] 170 | - Value(type_of(pos.moved_piece(m))) + HistoryStats::Max; 171 | else 172 | m.value = history[pos.moved_piece(m)][to_sq(m)] + fromTo.get(c, m); 173 | } 174 | 175 | 176 | /// next_move() is the most important method of the MovePicker class. It returns 177 | /// a new pseudo legal move every time it is called, until there are no more moves 178 | /// left. It picks the move with the biggest value from a list of generated moves 179 | /// taking care not to return the ttMove if it has already been searched. 180 | 181 | Move MovePicker::next_move() { 182 | 183 | Move move; 184 | 185 | switch (stage) { 186 | 187 | case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS: 188 | case QSEARCH_NO_CHECKS: case PROBCUT: 189 | ++stage; 190 | return ttMove; 191 | 192 | case CAPTURES_INIT: 193 | endBadCaptures = cur = moves; 194 | endMoves = generate(pos, cur); 195 | score(); 196 | ++stage; 197 | 198 | case GOOD_CAPTURES: 199 | while (cur < endMoves) 200 | { 201 | move = pick_best(cur++, endMoves); 202 | if (move != ttMove) 203 | { 204 | if (pos.see_ge(move, VALUE_ZERO)) 205 | return move; 206 | 207 | // Losing capture, move it to the beginning of the array 208 | *endBadCaptures++ = move; 209 | } 210 | } 211 | 212 | ++stage; 213 | move = ss->killers[0]; // First killer move 214 | if ( move != MOVE_NONE 215 | && move != ttMove 216 | && pos.pseudo_legal(move) 217 | && !pos.capture(move)) 218 | return move; 219 | 220 | case KILLERS: 221 | ++stage; 222 | move = ss->killers[1]; // Second killer move 223 | if ( move != MOVE_NONE 224 | && move != ttMove 225 | && pos.pseudo_legal(move) 226 | && !pos.capture(move)) 227 | return move; 228 | 229 | case COUNTERMOVE: 230 | ++stage; 231 | move = countermove; 232 | if ( move != MOVE_NONE 233 | && move != ttMove 234 | && move != ss->killers[0] 235 | && move != ss->killers[1] 236 | && pos.pseudo_legal(move) 237 | && !pos.capture(move)) 238 | return move; 239 | 240 | case QUIET_INIT: 241 | cur = endBadCaptures; 242 | endMoves = generate(pos, cur); 243 | score(); 244 | if (depth < 3 * ONE_PLY) 245 | { 246 | ExtMove* goodQuiet = std::partition(cur, endMoves, [](const ExtMove& m) 247 | { return m.value > VALUE_ZERO; }); 248 | insertion_sort(cur, goodQuiet); 249 | } else 250 | insertion_sort(cur, endMoves); 251 | ++stage; 252 | 253 | case QUIET: 254 | while (cur < endMoves) 255 | { 256 | move = *cur++; 257 | if ( move != ttMove 258 | && move != ss->killers[0] 259 | && move != ss->killers[1] 260 | && move != countermove) 261 | return move; 262 | } 263 | ++stage; 264 | cur = moves; // Point to beginning of bad captures 265 | 266 | case BAD_CAPTURES: 267 | if (cur < endBadCaptures) 268 | return *cur++; 269 | break; 270 | 271 | case EVASIONS_INIT: 272 | cur = moves; 273 | endMoves = generate(pos, cur); 274 | score(); 275 | ++stage; 276 | 277 | case ALL_EVASIONS: 278 | while (cur < endMoves) 279 | { 280 | move = pick_best(cur++, endMoves); 281 | if (move != ttMove) 282 | return move; 283 | } 284 | break; 285 | 286 | case PROBCUT_INIT: 287 | cur = moves; 288 | endMoves = generate(pos, cur); 289 | score(); 290 | ++stage; 291 | 292 | case PROBCUT_CAPTURES: 293 | while (cur < endMoves) 294 | { 295 | move = pick_best(cur++, endMoves); 296 | if ( move != ttMove 297 | && pos.see_ge(move, threshold + 1)) 298 | return move; 299 | } 300 | break; 301 | 302 | case QCAPTURES_1_INIT: case QCAPTURES_2_INIT: 303 | cur = moves; 304 | endMoves = generate(pos, cur); 305 | score(); 306 | ++stage; 307 | 308 | case QCAPTURES_1: case QCAPTURES_2: 309 | while (cur < endMoves) 310 | { 311 | move = pick_best(cur++, endMoves); 312 | if (move != ttMove) 313 | return move; 314 | } 315 | if (stage == QCAPTURES_2) 316 | break; 317 | cur = moves; 318 | endMoves = generate(pos, cur); 319 | ++stage; 320 | 321 | case QCHECKS: 322 | while (cur < endMoves) 323 | { 324 | move = cur++->move; 325 | if (move != ttMove) 326 | return move; 327 | } 328 | break; 329 | 330 | case QSEARCH_RECAPTURES: 331 | cur = moves; 332 | endMoves = generate(pos, cur); 333 | score(); 334 | ++stage; 335 | 336 | case QRECAPTURES: 337 | while (cur < endMoves) 338 | { 339 | move = pick_best(cur++, endMoves); 340 | if (to_sq(move) == recaptureSquare) 341 | return move; 342 | } 343 | break; 344 | 345 | default: 346 | assert(false); 347 | } 348 | 349 | return MOVE_NONE; 350 | } 351 | -------------------------------------------------------------------------------- /src/movepick.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef MOVEPICK_H_INCLUDED 22 | #define MOVEPICK_H_INCLUDED 23 | 24 | #include // For std::max 25 | #include // For std::memset 26 | 27 | #include "movegen.h" 28 | #include "position.h" 29 | #include "types.h" 30 | 31 | 32 | /// The Stats struct stores moves statistics. According to the template parameter 33 | /// the class can store History and Countermoves. History records how often 34 | /// different moves have been successful or unsuccessful during the current search 35 | /// and is used for reduction and move ordering decisions. 36 | /// Countermoves store the move that refute a previous one. Entries are stored 37 | /// using only the moving piece and destination square, hence two moves with 38 | /// different origin but same destination and piece will be considered identical. 39 | template 40 | struct Stats { 41 | 42 | static const Value Max = Value(1 << 28); 43 | 44 | const T* operator[](Piece pc) const { return table[pc]; } 45 | T* operator[](Piece pc) { return table[pc]; } 46 | void clear() { std::memset(table, 0, sizeof(table)); } 47 | void update(Piece pc, Square to, Move m) { table[pc][to] = m; } 48 | void update(Piece pc, Square to, Value v) { 49 | 50 | if (abs(int(v)) >= 324) 51 | return; 52 | 53 | table[pc][to] -= table[pc][to] * abs(int(v)) / (CM ? 936 : 324); 54 | table[pc][to] += int(v) * 32; 55 | } 56 | 57 | private: 58 | T table[PIECE_NB][SQUARE_NB]; 59 | }; 60 | 61 | typedef Stats MoveStats; 62 | typedef Stats HistoryStats; 63 | typedef Stats CounterMoveStats; 64 | typedef Stats CounterMoveHistoryStats; 65 | 66 | struct FromToStats { 67 | 68 | Value get(Color c, Move m) const { return table[c][from_sq(m)][to_sq(m)]; } 69 | void clear() { std::memset(table, 0, sizeof(table)); } 70 | void update(Color c, Move m, Value v) { 71 | 72 | if (abs(int(v)) >= 324) 73 | return; 74 | 75 | Square from = from_sq(m); 76 | Square to = to_sq(m); 77 | 78 | table[c][from][to] -= table[c][from][to] * abs(int(v)) / 324; 79 | table[c][from][to] += int(v) * 32; 80 | } 81 | 82 | private: 83 | Value table[COLOR_NB][SQUARE_NB][SQUARE_NB]; 84 | }; 85 | 86 | 87 | /// MovePicker class is used to pick one pseudo legal move at a time from the 88 | /// current position. The most important method is next_move(), which returns a 89 | /// new pseudo legal move each time it is called, until there are no moves left, 90 | /// when MOVE_NONE is returned. In order to improve the efficiency of the alpha 91 | /// beta algorithm, MovePicker attempts to return the moves which are most likely 92 | /// to get a cut-off first. 93 | namespace Search { struct Stack; } 94 | 95 | class MovePicker { 96 | public: 97 | MovePicker(const MovePicker&) = delete; 98 | MovePicker& operator=(const MovePicker&) = delete; 99 | 100 | MovePicker(const Position&, Move, Value); 101 | MovePicker(const Position&, Move, Depth, Square); 102 | MovePicker(const Position&, Move, Depth, Search::Stack*); 103 | 104 | Move next_move(); 105 | 106 | private: 107 | template void score(); 108 | ExtMove* begin() { return cur; } 109 | ExtMove* end() { return endMoves; } 110 | 111 | const Position& pos; 112 | const Search::Stack* ss; 113 | Move countermove; 114 | Depth depth; 115 | Move ttMove; 116 | Square recaptureSquare; 117 | Value threshold; 118 | int stage; 119 | ExtMove *cur, *endMoves, *endBadCaptures; 120 | ExtMove moves[MAX_MOVES]; 121 | }; 122 | 123 | #endif // #ifndef MOVEPICK_H_INCLUDED 124 | -------------------------------------------------------------------------------- /src/pawns.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include "bitboard.h" 25 | #include "pawns.h" 26 | #include "position.h" 27 | #include "thread.h" 28 | 29 | namespace { 30 | 31 | #define V Value 32 | #define S(mg, eg) make_score(mg, eg) 33 | 34 | // Isolated pawn penalty by opposed flag 35 | const Score Isolated[2] = { S(45, 40), S(30, 27) }; 36 | 37 | // Backward pawn penalty by opposed flag 38 | const Score Backward[2] = { S(56, 33), S(41, 19) }; 39 | 40 | // Unsupported pawn penalty for pawns which are neither isolated or backward 41 | const Score Unsupported = S(17, 8); 42 | 43 | // Connected pawn bonus by opposed, phalanx, twice supported and rank 44 | Score Connected[2][2][2][RANK_NB]; 45 | 46 | // Doubled pawn penalty 47 | const Score Doubled = S(18,38); 48 | 49 | // Lever bonus by rank 50 | const Score Lever[RANK_NB] = { 51 | S( 0, 0), S( 0, 0), S(0, 0), S(0, 0), 52 | S(17, 16), S(33, 32), S(0, 0), S(0, 0) 53 | }; 54 | 55 | // Weakness of our pawn shelter in front of the king by [distance from edge][rank] 56 | const Value ShelterWeakness[][RANK_NB] = { 57 | { V(100), V(20), V(10), V(46), V(82), V( 86), V( 98) }, 58 | { V(116), V( 4), V(28), V(87), V(94), V(108), V(104) }, 59 | { V(109), V( 1), V(59), V(87), V(62), V( 91), V(116) }, 60 | { V( 75), V(12), V(43), V(59), V(90), V( 84), V(112) } 61 | }; 62 | 63 | // Danger of enemy pawns moving toward our king by [type][distance from edge][rank] 64 | const Value StormDanger[][4][RANK_NB] = { 65 | { { V( 4), V( 73), V( 132), V(46), V(31) }, 66 | { V( 1), V( 64), V( 143), V(26), V(13) }, 67 | { V( 1), V( 47), V( 110), V(44), V(24) }, 68 | { V( 0), V( 72), V( 127), V(50), V(31) } }, 69 | { { V(22), V( 45), V( 104), V(62), V( 6) }, 70 | { V(31), V( 30), V( 99), V(39), V(19) }, 71 | { V(23), V( 29), V( 96), V(41), V(15) }, 72 | { V(21), V( 23), V( 116), V(41), V(15) } }, 73 | { { V( 0), V( 0), V( 79), V(23), V( 1) }, 74 | { V( 0), V( 0), V( 148), V(27), V( 2) }, 75 | { V( 0), V( 0), V( 161), V(16), V( 1) }, 76 | { V( 0), V( 0), V( 171), V(22), V(15) } }, 77 | { { V( 0), V(-290), V(-274), V(57), V(41) }, 78 | { V( 0), V( 60), V( 144), V(39), V(13) }, 79 | { V( 0), V( 65), V( 141), V(41), V(34) }, 80 | { V( 0), V( 53), V( 127), V(56), V(14) } } 81 | 82 | }; 83 | 84 | // Max bonus for king safety. Corresponds to start position with all the pawns 85 | // in front of the king and no enemy pawn on the horizon. 86 | const Value MaxSafetyBonus = V(258); 87 | 88 | #undef S 89 | #undef V 90 | 91 | template 92 | Score evaluate(const Position& pos, Pawns::Entry* e) { 93 | 94 | const Color Them = (Us == WHITE ? BLACK : WHITE); 95 | const Square Up = (Us == WHITE ? NORTH : SOUTH); 96 | const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); 97 | const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); 98 | 99 | Bitboard b, neighbours, stoppers, doubled, supported, phalanx; 100 | Square s; 101 | bool opposed, lever, connected, backward; 102 | Score score = SCORE_ZERO; 103 | const Square* pl = pos.squares(Us); 104 | const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)]; 105 | 106 | Bitboard ourPawns = pos.pieces(Us , PAWN); 107 | Bitboard theirPawns = pos.pieces(Them, PAWN); 108 | 109 | e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0; 110 | e->semiopenFiles[Us] = 0xFF; 111 | e->kingSquares[Us] = SQ_NONE; 112 | e->pawnAttacks[Us] = shift(ourPawns) | shift(ourPawns); 113 | e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares); 114 | e->pawnsOnSquares[Us][WHITE] = pos.count(Us) - e->pawnsOnSquares[Us][BLACK]; 115 | 116 | // Loop through all pawns of the current color and score each pawn 117 | while ((s = *pl++) != SQ_NONE) 118 | { 119 | assert(pos.piece_on(s) == make_piece(Us, PAWN)); 120 | 121 | File f = file_of(s); 122 | 123 | e->semiopenFiles[Us] &= ~(1 << f); 124 | e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); 125 | 126 | // Flag the pawn 127 | opposed = theirPawns & forward_bb(Us, s); 128 | stoppers = theirPawns & passed_pawn_mask(Us, s); 129 | lever = theirPawns & pawnAttacksBB[s]; 130 | doubled = ourPawns & (s + Up); 131 | neighbours = ourPawns & adjacent_files_bb(f); 132 | phalanx = neighbours & rank_bb(s); 133 | supported = neighbours & rank_bb(s - Up); 134 | connected = supported | phalanx; 135 | 136 | // A pawn is backward when it is behind all pawns of the same color on the 137 | // adjacent files and cannot be safely advanced. 138 | if (!neighbours || lever || relative_rank(Us, s) >= RANK_5) 139 | backward = false; 140 | else 141 | { 142 | // Find the backmost rank with neighbours or stoppers 143 | b = rank_bb(backmost_sq(Us, neighbours | stoppers)); 144 | 145 | // The pawn is backward when it cannot safely progress to that rank: 146 | // either there is a stopper in the way on this rank, or there is a 147 | // stopper on adjacent file which controls the way to that rank. 148 | backward = (b | shift(b & adjacent_files_bb(f))) & stoppers; 149 | 150 | assert(!backward || !(pawn_attack_span(Them, s + Up) & neighbours)); 151 | } 152 | 153 | // Passed pawns will be properly scored in evaluation because we need 154 | // full attack info to evaluate them. 155 | if (!stoppers && !(ourPawns & forward_bb(Us, s))) 156 | e->passedPawns[Us] |= s; 157 | 158 | // Score this pawn 159 | if (!neighbours) 160 | score -= Isolated[opposed]; 161 | 162 | else if (backward) 163 | score -= Backward[opposed]; 164 | 165 | else if (!supported) 166 | score -= Unsupported; 167 | 168 | if (connected) 169 | score += Connected[opposed][!!phalanx][more_than_one(supported)][relative_rank(Us, s)]; 170 | 171 | if (doubled) 172 | score -= Doubled; 173 | 174 | if (lever) 175 | score += Lever[relative_rank(Us, s)]; 176 | } 177 | 178 | return score; 179 | } 180 | 181 | } // namespace 182 | 183 | namespace Pawns { 184 | 185 | /// Pawns::init() initializes some tables needed by evaluation. Instead of using 186 | /// hard-coded tables, when makes sense, we prefer to calculate them with a formula 187 | /// to reduce independent parameters and to allow easier tuning and better insight. 188 | 189 | void init() { 190 | 191 | static const int Seed[RANK_NB] = { 0, 8, 19, 13, 71, 94, 169, 324 }; 192 | 193 | for (int opposed = 0; opposed <= 1; ++opposed) 194 | for (int phalanx = 0; phalanx <= 1; ++phalanx) 195 | for (int apex = 0; apex <= 1; ++apex) 196 | for (Rank r = RANK_2; r < RANK_8; ++r) 197 | { 198 | int v = (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed; 199 | v += (apex ? v / 2 : 0); 200 | Connected[opposed][phalanx][apex][r] = make_score(v, v * 5 / 8); 201 | } 202 | } 203 | 204 | 205 | /// Pawns::probe() looks up the current position's pawns configuration in 206 | /// the pawns hash table. It returns a pointer to the Entry if the position 207 | /// is found. Otherwise a new Entry is computed and stored there, so we don't 208 | /// have to recompute all when the same pawns configuration occurs again. 209 | 210 | Entry* probe(const Position& pos) { 211 | 212 | Key key = pos.pawn_key(); 213 | Entry* e = pos.this_thread()->pawnsTable[key]; 214 | 215 | if (e->key == key) 216 | return e; 217 | 218 | e->key = key; 219 | e->score = evaluate(pos, e) - evaluate(pos, e); 220 | e->asymmetry = popcount(e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]); 221 | e->openFiles = popcount(e->semiopenFiles[WHITE] & e->semiopenFiles[BLACK]); 222 | return e; 223 | } 224 | 225 | 226 | /// Entry::shelter_storm() calculates shelter and storm penalties for the file 227 | /// the king is on, as well as the two adjacent files. 228 | 229 | template 230 | Value Entry::shelter_storm(const Position& pos, Square ksq) { 231 | 232 | const Color Them = (Us == WHITE ? BLACK : WHITE); 233 | 234 | enum { NoFriendlyPawn, Unblocked, BlockedByPawn, BlockedByKing }; 235 | 236 | Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq)); 237 | Bitboard ourPawns = b & pos.pieces(Us); 238 | Bitboard theirPawns = b & pos.pieces(Them); 239 | Value safety = MaxSafetyBonus; 240 | File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq))); 241 | 242 | for (File f = center - File(1); f <= center + File(1); ++f) 243 | { 244 | b = ourPawns & file_bb(f); 245 | Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; 246 | 247 | b = theirPawns & file_bb(f); 248 | Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; 249 | 250 | safety -= ShelterWeakness[std::min(f, FILE_H - f)][rkUs] 251 | + StormDanger 252 | [f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing : 253 | rkUs == RANK_1 ? NoFriendlyPawn : 254 | rkThem == rkUs + 1 ? BlockedByPawn : Unblocked] 255 | [std::min(f, FILE_H - f)][rkThem]; 256 | } 257 | 258 | return safety; 259 | } 260 | 261 | 262 | /// Entry::do_king_safety() calculates a bonus for king safety. It is called only 263 | /// when king square changes, which is about 20% of total king_safety() calls. 264 | 265 | template 266 | Score Entry::do_king_safety(const Position& pos, Square ksq) { 267 | 268 | kingSquares[Us] = ksq; 269 | castlingRights[Us] = pos.can_castle(Us); 270 | int minKingPawnDistance = 0; 271 | 272 | Bitboard pawns = pos.pieces(Us, PAWN); 273 | if (pawns) 274 | while (!(DistanceRingBB[ksq][minKingPawnDistance++] & pawns)) {} 275 | 276 | Value bonus = shelter_storm(pos, ksq); 277 | 278 | // If we can castle use the bonus after the castling if it is bigger 279 | if (pos.can_castle(MakeCastling::right)) 280 | bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_G1))); 281 | 282 | if (pos.can_castle(MakeCastling::right)) 283 | bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_C1))); 284 | 285 | return make_score(bonus, -16 * minKingPawnDistance); 286 | } 287 | 288 | // Explicit template instantiation 289 | template Score Entry::do_king_safety(const Position& pos, Square ksq); 290 | template Score Entry::do_king_safety(const Position& pos, Square ksq); 291 | 292 | } // namespace Pawns 293 | -------------------------------------------------------------------------------- /src/pawns.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef PAWNS_H_INCLUDED 22 | #define PAWNS_H_INCLUDED 23 | 24 | #include "misc.h" 25 | #include "position.h" 26 | #include "types.h" 27 | 28 | namespace Pawns { 29 | 30 | /// Pawns::Entry contains various information about a pawn structure. A lookup 31 | /// to the pawn hash table (performed by calling the probe function) returns a 32 | /// pointer to an Entry object. 33 | 34 | struct Entry { 35 | 36 | Score pawns_score() const { return score; } 37 | Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } 38 | Bitboard passed_pawns(Color c) const { return passedPawns[c]; } 39 | Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } 40 | int pawn_asymmetry() const { return asymmetry; } 41 | int open_files() const { return openFiles; } 42 | 43 | int semiopen_file(Color c, File f) const { 44 | return semiopenFiles[c] & (1 << f); 45 | } 46 | 47 | int semiopen_side(Color c, File f, bool leftSide) const { 48 | return semiopenFiles[c] & (leftSide ? (1 << f) - 1 : ~((1 << (f + 1)) - 1)); 49 | } 50 | 51 | int pawns_on_same_color_squares(Color c, Square s) const { 52 | return pawnsOnSquares[c][!!(DarkSquares & s)]; 53 | } 54 | 55 | template 56 | Score king_safety(const Position& pos, Square ksq) { 57 | return kingSquares[Us] == ksq && castlingRights[Us] == pos.can_castle(Us) 58 | ? kingSafety[Us] : (kingSafety[Us] = do_king_safety(pos, ksq)); 59 | } 60 | 61 | template 62 | Score do_king_safety(const Position& pos, Square ksq); 63 | 64 | template 65 | Value shelter_storm(const Position& pos, Square ksq); 66 | 67 | Key key; 68 | Score score; 69 | Bitboard passedPawns[COLOR_NB]; 70 | Bitboard pawnAttacks[COLOR_NB]; 71 | Bitboard pawnAttacksSpan[COLOR_NB]; 72 | Square kingSquares[COLOR_NB]; 73 | Score kingSafety[COLOR_NB]; 74 | int castlingRights[COLOR_NB]; 75 | int semiopenFiles[COLOR_NB]; 76 | int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] 77 | int asymmetry; 78 | int openFiles; 79 | }; 80 | 81 | typedef HashTable Table; 82 | 83 | void init(); 84 | Entry* probe(const Position& pos); 85 | 86 | } // namespace Pawns 87 | 88 | #endif // #ifndef PAWNS_H_INCLUDED 89 | -------------------------------------------------------------------------------- /src/psqt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | 23 | #include "types.h" 24 | 25 | Value PieceValue[PHASE_NB][PIECE_NB] = { 26 | { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, 27 | { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } 28 | }; 29 | 30 | namespace PSQT { 31 | 32 | #define S(mg, eg) make_score(mg, eg) 33 | 34 | // Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece 35 | // type on a given square a (middlegame, endgame) score pair is assigned. Table 36 | // is defined for files A..D and white side: it is symmetric for black side and 37 | // second half of the files. 38 | const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { 39 | { }, 40 | { // Pawn 41 | { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }, 42 | { S(-16, 7), S( 1,-4), S( 7, 8), S( 3,-2) }, 43 | { S(-23,-4), S( -7,-5), S( 19, 5), S(24, 4) }, 44 | { S(-22, 3), S(-14, 3), S( 20,-8), S(35,-3) }, 45 | { S(-11, 8), S( 0, 9), S( 3, 7), S(21,-6) }, 46 | { S(-11, 8), S(-13,-5), S( -6, 2), S(-2, 4) }, 47 | { S( -9, 3), S( 15,-9), S( -8, 1), S(-4,18) } 48 | }, 49 | { // Knight 50 | { S(-143, -97), S(-96,-82), S(-80,-46), S(-73,-14) }, 51 | { S( -83, -69), S(-43,-55), S(-21,-17), S(-10, 9) }, 52 | { S( -71, -50), S(-22,-39), S( 0, -8), S( 9, 28) }, 53 | { S( -25, -41), S( 18,-25), S( 43, 7), S( 47, 38) }, 54 | { S( -26, -46), S( 16,-25), S( 38, 2), S( 50, 41) }, 55 | { S( -11, -55), S( 37,-38), S( 56, -8), S( 71, 27) }, 56 | { S( -62, -64), S(-17,-50), S( 5,-24), S( 14, 13) }, 57 | { S(-195,-110), S(-66,-90), S(-42,-50), S(-29,-13) } 58 | }, 59 | { // Bishop 60 | { S(-54,-68), S(-23,-40), S(-35,-46), S(-44,-28) }, 61 | { S(-30,-43), S( 10,-17), S( 2,-23), S( -9, -5) }, 62 | { S(-19,-32), S( 17, -9), S( 11,-13), S( 1, 8) }, 63 | { S(-21,-36), S( 18,-13), S( 11,-15), S( 0, 7) }, 64 | { S(-21,-36), S( 14,-14), S( 6,-17), S( -1, 3) }, 65 | { S(-27,-35), S( 6,-13), S( 2,-10), S( -8, 1) }, 66 | { S(-33,-44), S( 7,-21), S( -4,-22), S(-12, -4) }, 67 | { S(-45,-65), S(-21,-42), S(-29,-46), S(-39,-27) } 68 | }, 69 | { // Rook 70 | { S(-25, 0), S(-16, 0), S(-16, 0), S(-9, 0) }, 71 | { S(-21, 0), S( -8, 0), S( -3, 0), S( 0, 0) }, 72 | { S(-21, 0), S( -9, 0), S( -4, 0), S( 2, 0) }, 73 | { S(-22, 0), S( -6, 0), S( -1, 0), S( 2, 0) }, 74 | { S(-22, 0), S( -7, 0), S( 0, 0), S( 1, 0) }, 75 | { S(-21, 0), S( -7, 0), S( 0, 0), S( 2, 0) }, 76 | { S(-12, 0), S( 4, 0), S( 8, 0), S(12, 0) }, 77 | { S(-23, 0), S(-15, 0), S(-11, 0), S(-5, 0) } 78 | }, 79 | { // Queen 80 | { S( 0,-70), S(-3,-57), S(-4,-41), S(-1,-29) }, 81 | { S(-4,-58), S( 6,-30), S( 9,-21), S( 8, -4) }, 82 | { S(-2,-39), S( 6,-17), S( 9, -7), S( 9, 5) }, 83 | { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 17) }, 84 | { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 23) }, 85 | { S(-2,-40), S( 6,-16), S( 8,-11), S(10, 3) }, 86 | { S(-2,-54), S( 7,-30), S( 7,-21), S( 6, -7) }, 87 | { S(-1,-75), S(-4,-54), S(-1,-44), S( 0,-30) } 88 | }, 89 | { // King 90 | { S(291, 28), S(344, 76), S(294,103), S(219,112) }, 91 | { S(289, 70), S(329,119), S(263,170), S(205,159) }, 92 | { S(226,109), S(271,164), S(202,195), S(136,191) }, 93 | { S(204,131), S(212,194), S(175,194), S(137,204) }, 94 | { S(177,132), S(205,187), S(143,224), S( 94,227) }, 95 | { S(147,118), S(188,178), S(113,199), S( 70,197) }, 96 | { S(116, 72), S(158,121), S( 93,142), S( 48,161) }, 97 | { S( 94, 30), S(120, 76), S( 78,101), S( 31,111) } 98 | } 99 | }; 100 | 101 | #undef S 102 | 103 | Score psq[PIECE_NB][SQUARE_NB]; 104 | 105 | // init() initializes piece-square tables: the white halves of the tables are 106 | // copied from Bonus[] adding the piece value, then the black halves of the 107 | // tables are initialized by flipping and changing the sign of the white scores. 108 | void init() { 109 | 110 | for (Piece pc = W_PAWN; pc <= W_KING; ++pc) 111 | { 112 | PieceValue[MG][~pc] = PieceValue[MG][pc]; 113 | PieceValue[EG][~pc] = PieceValue[EG][pc]; 114 | 115 | Score v = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); 116 | 117 | for (Square s = SQ_A1; s <= SQ_H8; ++s) 118 | { 119 | File f = std::min(file_of(s), FILE_H - file_of(s)); 120 | psq[ pc][ s] = v + Bonus[pc][rank_of(s)][f]; 121 | psq[~pc][~s] = -psq[pc][s]; 122 | } 123 | } 124 | } 125 | 126 | } // namespace PSQT 127 | -------------------------------------------------------------------------------- /src/scoutfish.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import json 4 | import os 5 | import pexpect 6 | import re 7 | from pexpect.popen_spawn import PopenSpawn 8 | 9 | PGN_HEADERS_REGEX = re.compile(r"\[([A-Za-z0-9_]+)\s+\"(.*)\"\]") 10 | 11 | 12 | class Scoutfish: 13 | def __init__(self, engine=''): 14 | if not engine: 15 | engine = './scoutfish' 16 | self.p = PopenSpawn(engine, encoding="utf-8") 17 | self.wait_ready() 18 | self.pgn = '' 19 | self.db = '' 20 | 21 | def wait_ready(self): 22 | self.p.sendline('isready') 23 | self.p.expect(u'readyok') 24 | 25 | def open(self, pgn): 26 | '''Open a PGN file and create an index if not exsisting''' 27 | if not os.path.isfile(pgn): 28 | raise NameError("File {} does not exsist".format(pgn)) 29 | pgn = os.path.normcase(pgn) 30 | self.pgn = pgn 31 | self.db = os.path.splitext(pgn)[0] + '.scout' 32 | if not os.path.isfile(self.db): 33 | result = self.make() 34 | self.db = result['DB file'] 35 | 36 | def close(self): 37 | '''Terminate scoutfish. Not really needed: engine will terminate as 38 | soon as pipe is closed, i.e. when we exit.''' 39 | self.p.sendline('quit') 40 | self.p.expect(pexpect.EOF) 41 | self.pgn = '' 42 | self.db = '' 43 | 44 | def make(self): 45 | '''Make an index out of a pgn file. Normally called by open()''' 46 | if not self.pgn: 47 | raise NameError("Unknown DB, first open a PGN file") 48 | cmd = 'make ' + self.pgn 49 | self.p.sendline(cmd) 50 | self.wait_ready() 51 | s = '{' + self.p.before.split('{')[1] 52 | s = s.replace('\\', r'\\') # Escape Windows's path delimiter 53 | result = json.loads(s) 54 | self.p.before = '' 55 | return result 56 | 57 | def setoption(self, name, value): 58 | '''Set an option value, like threads number''' 59 | cmd = "setoption name {} value {}".format(name, value) 60 | self.p.sendline(cmd) 61 | self.wait_ready() 62 | 63 | def scout(self, q): 64 | '''Run query defined by 'q' dict. Result will be a dict too''' 65 | if not self.db: 66 | raise NameError("Unknown DB, first open a PGN file") 67 | j = json.dumps(q) 68 | cmd = "scout {} {}".format(self.db, j) 69 | self.p.sendline(cmd) 70 | self.wait_ready() 71 | result = json.loads(self.p.before) 72 | self.p.before = '' 73 | return result 74 | 75 | def scout_raw(self, q): 76 | '''Run query defined by 'q' dict. Result will be full output''' 77 | if not self.db: 78 | raise NameError("Unknown DB, first open a PGN file") 79 | j = json.dumps(q) 80 | cmd = "scout {} {}".format(self.db, j) 81 | self.p.sendline(cmd) 82 | self.wait_ready() 83 | result = self.p.before 84 | self.p.before = '' 85 | return result 86 | 87 | def get_games(self, matches): 88 | '''Retrieve the PGN games specified in the offset list. Games are 89 | added to each list item with a 'pgn' key''' 90 | if not self.pgn: 91 | raise NameError("Unknown DB, first open a PGN file") 92 | with open(self.pgn, "rU") as f: 93 | for match in matches: 94 | f.seek(match['ofs']) 95 | game = '' 96 | for line in f: 97 | if line.startswith('[Event "'): 98 | if game: 99 | break # Second one, start of next game 100 | else: 101 | game = line # First occurence 102 | elif game: 103 | game += line 104 | match['pgn'] = game.strip() 105 | return matches 106 | 107 | def get_header(self, pgn): 108 | '''Return a dict with just header information out of a pgn game. The 109 | pgn tags are supposed to be consecutive''' 110 | header = {} 111 | for line in pgn.splitlines(): 112 | line = line.strip() 113 | if line.startswith('[') and line.endswith(']'): 114 | tag_match = PGN_HEADERS_REGEX.match(line) 115 | if tag_match: 116 | header[tag_match.group(1)] = tag_match.group(2) 117 | else: 118 | break 119 | return header 120 | 121 | def get_game_headers(self, matches): 122 | '''Return a list of headers out of a list of pgn games. It is defined 123 | to be compatible with the return value of get_games()''' 124 | headers = [] 125 | for match in matches: 126 | pgn = match['pgn'] 127 | h = self.get_header(pgn) 128 | headers.append(h) 129 | return headers 130 | -------------------------------------------------------------------------------- /src/search.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef SEARCH_H_INCLUDED 22 | #define SEARCH_H_INCLUDED 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "misc.h" 29 | #include "movepick.h" 30 | #include "types.h" 31 | 32 | class Position; 33 | 34 | namespace Scout { 35 | 36 | enum GameResult : uint8_t { 37 | WhiteWin = 1, BlackWin, Draw, Unknown, Invalid 38 | }; 39 | 40 | enum ResultType { 41 | ResultNone, ResultMate, ResultStalemate 42 | }; 43 | 44 | enum RuleType { 45 | RuleNone, RulePass, RuleResult, RuleResultType, RuleSubFen, RuleMaterial, 46 | RuleImbalance, RuleMove, RuleQuietMove, RuleCapturedPiece, RuleMovedPiece, 47 | RuleWhite, RuleBlack, RuleMatchedCondition, RuleMatchedQuery 48 | }; 49 | 50 | struct SubFen { 51 | Bitboard white, black; 52 | std::vector> pieces; 53 | }; 54 | 55 | struct ScoutMove { 56 | Piece pc; 57 | Square to; 58 | int disambiguation; 59 | PieceType promotion; 60 | bool castle; 61 | }; 62 | 63 | struct Imbalance { 64 | Value nonPawnMaterial; 65 | int pawnCount; 66 | }; 67 | 68 | struct Condition { 69 | Bitboard moveSquares; 70 | int streakId; 71 | ResultType resultType; 72 | uint64_t movedFlags, capturedFlags; 73 | std::vector rules; 74 | std::vector subfens; 75 | std::vector results; 76 | std::vector moves; 77 | std::vector matKeys; 78 | std::vector imbalances; 79 | }; 80 | 81 | struct MatchingGame { 82 | uint64_t gameOfs; 83 | std::vector plies; 84 | }; 85 | 86 | struct Data { 87 | Move* baseAddress; 88 | size_t dbMapping, dbSize; 89 | size_t skip, limit, movesCnt; 90 | std::vector conditions; 91 | std::vector matches; 92 | }; 93 | 94 | } 95 | 96 | namespace Search { 97 | 98 | /// Stack struct keeps track of the information we need to remember from nodes 99 | /// shallower and deeper in the tree during the search. Each search thread has 100 | /// its own array of Stack objects, indexed by the current ply. 101 | 102 | struct Stack { 103 | Move* pv; 104 | int ply; 105 | Move currentMove; 106 | Move excludedMove; 107 | Move killers[2]; 108 | Value staticEval; 109 | Value history; 110 | bool skipEarlyPruning; 111 | int moveCount; 112 | CounterMoveStats* counterMoves; 113 | }; 114 | 115 | 116 | /// RootMove struct is used for moves at the root of the tree. For each root move 117 | /// we store a score and a PV (really a refutation in the case of moves which 118 | /// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. 119 | 120 | struct RootMove { 121 | 122 | explicit RootMove(Move m) : pv(1, m) {} 123 | 124 | bool operator<(const RootMove& m) const { return m.score < score; } // Descending sort 125 | bool operator==(const Move& m) const { return pv[0] == m; } 126 | bool extract_ponder_from_tt(Position& pos); 127 | 128 | Value score = -VALUE_INFINITE; 129 | Value previousScore = -VALUE_INFINITE; 130 | std::vector pv; 131 | }; 132 | 133 | typedef std::vector RootMoves; 134 | 135 | 136 | /// LimitsType struct stores information sent by GUI about available time to 137 | /// search the current move, maximum depth/time, if we are in analysis mode or 138 | /// if we have to ponder while it's our opponent's turn to move. 139 | 140 | struct LimitsType { 141 | 142 | LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC 143 | nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = 144 | npmsec = movestogo = depth = movetime = mate = infinite = ponder = 0; 145 | scout = Scout::Data(); 146 | } 147 | 148 | bool use_time_management() const { 149 | return !(mate | movetime | depth | nodes | infinite); 150 | } 151 | 152 | std::vector searchmoves; 153 | int time[COLOR_NB], inc[COLOR_NB], npmsec, movestogo, depth, movetime, mate, infinite, ponder; 154 | int64_t nodes; 155 | TimePoint startTime; 156 | Scout::Data scout; 157 | }; 158 | 159 | 160 | /// SignalsType struct stores atomic flags updated during the search, typically 161 | /// in an async fashion e.g. to stop the search by the GUI. 162 | 163 | struct SignalsType { 164 | std::atomic_bool stop, stopOnPonderhit; 165 | }; 166 | 167 | extern SignalsType Signals; 168 | extern LimitsType Limits; 169 | 170 | void init(); 171 | void clear(); 172 | template uint64_t perft(Position& pos, Depth depth); 173 | 174 | } // namespace Search 175 | 176 | 177 | class Thread; 178 | 179 | namespace Scout { 180 | 181 | void parse_query(Scout::Data&, std::istringstream&); 182 | void search(Thread*); 183 | void print_results(const Search::LimitsType&); 184 | 185 | } // namespace Scout 186 | 187 | #endif // #ifndef SEARCH_H_INCLUDED 188 | -------------------------------------------------------------------------------- /src/syzygy/tbprobe.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (c) 2013 Ronald de Man 4 | Copyright (C) 2016 Marco Costalba, Lucas Braesch 5 | 6 | Stockfish is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Stockfish is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifndef TBPROBE_H 21 | #define TBPROBE_H 22 | 23 | #include 24 | 25 | #include "../search.h" 26 | 27 | namespace Tablebases { 28 | 29 | enum WDLScore { 30 | WDLLoss = -2, // Loss 31 | WDLCursedLoss = -1, // Loss, but draw under 50-move rule 32 | WDLDraw = 0, // Draw 33 | WDLCursedWin = 1, // Win, but draw under 50-move rule 34 | WDLWin = 2, // Win 35 | 36 | WDLScoreNone = -1000 37 | }; 38 | 39 | // Possible states after a probing operation 40 | enum ProbeState { 41 | FAIL = 0, // Probe failed (missing file table) 42 | OK = 1, // Probe succesful 43 | CHANGE_STM = -1, // DTZ should check the other side 44 | ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move) 45 | }; 46 | 47 | extern int MaxCardinality; 48 | 49 | void init(const std::string& paths); 50 | WDLScore probe_wdl(Position& pos, ProbeState* result); 51 | int probe_dtz(Position& pos, ProbeState* result); 52 | bool root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score); 53 | bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score); 54 | void filter_root_moves(Position& pos, Search::RootMoves& rootMoves); 55 | 56 | inline std::ostream& operator<<(std::ostream& os, const WDLScore v) { 57 | 58 | os << (v == WDLLoss ? "Loss" : 59 | v == WDLCursedLoss ? "Cursed loss" : 60 | v == WDLDraw ? "Draw" : 61 | v == WDLCursedWin ? "Cursed win" : 62 | v == WDLWin ? "Win" : "None"); 63 | 64 | return os; 65 | } 66 | 67 | inline std::ostream& operator<<(std::ostream& os, const ProbeState v) { 68 | 69 | os << (v == FAIL ? "Failed" : 70 | v == OK ? "Success" : 71 | v == CHANGE_STM ? "Probed opponent side" : 72 | v == ZEROING_BEST_MOVE ? "Best move zeroes DTZ" : "None"); 73 | 74 | return os; 75 | } 76 | 77 | } 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /src/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import json 4 | import os 5 | import sys 6 | import unittest 7 | 8 | from scoutfish import Scoutfish 9 | 10 | 11 | SCOUTFISH = './scoutfish.exe' if 'nt' in os.name else './scoutfish' 12 | 13 | QUERIES = [ 14 | {'q': {'sub-fen': 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR', 'stm': 'white'}, 15 | 'count': 501, 'matches': [{'ofs': 0, 'ply': [0]}, {'ofs': 666, 'ply': [0]}]}, 16 | 17 | {'q': {'sub-fen': 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR', 'stm': 'balck'}, 18 | 'count': 229, 'matches': [{'ofs': 666, 'ply': [1]}, {'ofs': 2008, 'ply': [1]}]}, 19 | 20 | {'q': {'sub-fen': '8/8/p7/8/8/1B3N2/8/8'}, 21 | 'count': 29, 'matches': [{'ofs': 90547, 'ply': [11]}, {'ofs': 106231, 'ply': [13]}]}, 22 | 23 | {'q': {'sub-fen': '8/8/8/8/1k6/8/8/8', 'result': '1/2-1/2'}, 24 | 'count': 1, 'matches': [{'ofs': 810760, 'ply': [98]}]}, 25 | 26 | {'q': {'sub-fen': ['8/8/8/q7/8/8/8/8', '8/8/8/r7/8/8/8/8']}, 27 | 'count': 72, 'matches': [{'ofs': 42247, 'ply': [6]}, {'ofs': 45161, 'ply': [34]}]}, 28 | 29 | {'q': {'material': 'KQRRBNPPPPKQRRNNPPPP', 'stm': 'black'}, 30 | 'count': 2, 'matches': [{'ofs': 576617, 'ply': [49]}, {'ofs': 611693, 'ply': [43]}]}, 31 | 32 | {'q': {'material': 'KQRRBNNPPPPKQRRBNNPPPP', 'result': '0-1'}, 33 | 'count': 2, 'matches': [{'ofs': 611693, 'ply': [39]}, {'ofs': 692493, 'ply': [60]}]}, 34 | 35 | {'q': {'material': ['KRBPPPKRPPP', 'KRPPPKRPPP']}, 36 | 'count': 4, 'matches': [{'ofs': 666, 'ply': [77]}, {'ofs': 164246, 'ply': [83]}]}, 37 | 38 | {'q': {'white-move': 'Nb7'}, 39 | 'count': 6, 'matches': [{'ofs': 141745, 'ply': [34]}, {'ofs': 538533, 'ply': [36]}]}, 40 | 41 | {'q': {'black-move': 'c3'}, 42 | 'count': 27, 'matches': [{'ofs': 10226, 'ply': [33]}, {'ofs': 26360, 'ply': [7]}]}, 43 | 44 | {'q': {'black-move': 'e1=Q'}, 45 | 'count': 1, 'matches': [{'ofs': 666, 'ply': [103]}]}, 46 | 47 | {'q': {'black-move': 'O-O'}, 48 | 'count': 354, 'matches': [{'ofs': 0, 'ply': [15]}, {'ofs': 666, 'ply': [31]}]}, 49 | 50 | {'q': {'skip': 200, 'limit': 100, 'black-move': 'O-O'}, 51 | 'count': 100, 'matches': [{'ofs': 485616, 'ply': [15]}, {'ofs': 487518, 'ply': [11]}]}, 52 | 53 | {'q': {'black-move': 'O-O-O'}, 54 | 'count': 28, 'matches': [{'ofs': 10226, 'ply': [35]}, {'ofs': 64548, 'ply': [31]}]}, 55 | 56 | {'q': {'black-move': ['O-O-O', 'O-O']}, 57 | 'count': 382, 'matches': [{'ofs': 0, 'ply': [15]}, {'ofs': 666, 'ply': [31]}]}, 58 | 59 | {'q': {'white-move': ['a7', 'b7', 'Rac1']}, 60 | 'count': 120, 'matches': [{'ofs': 666, 'ply': [38]}, {'ofs': 2008, 'ply': [32]}, {'ofs': 10226, 'ply': [38]}]}, 61 | 62 | {'q': {'imbalance': 'vPP'}, 63 | 'count': 52, 'matches': [{'ofs': 3313, 'ply': [12]}, {'ofs': 8436, 'ply': [12]}]}, 64 | 65 | {'q': {'imbalance': ['BvN', 'NNvB']}, 66 | 'count': 142, 'matches': [{'ofs': 666, 'ply': [42]}, {'ofs': 16551, 'ply': [25]}]}, 67 | 68 | {'q': {'moved': 'KP', 'captured': 'Q', 'result': ['1-0', '0-1']}, 69 | 'count': 48, 'matches': [{'ofs': 666, 'ply': [45]}, {'ofs': 8436, 'ply': [41]}]}, 70 | 71 | {'q': {'result-type': 'mate', 'result': '0-1'}, 72 | 'count': 10, 'matches': [{'ofs': 11831, 'ply': [24]}, {'ofs': 30634, 'ply': [40]}]}, 73 | 74 | {'q': {'sub-fen': ['rnbqkbnr/pp1p1ppp/2p5/4p3/3PP3/8/PPP2PPP/RNBQKBNR', 75 | 'rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R']}, 76 | 'count': 50, 'matches': [{'ofs': 16551, 'ply': [3]}, {'ofs': 75579, 'ply': [3]}]}, 77 | 78 | {'q': {'sequence': [{'sub-fen': '8/3p4/8/8/8/8/8/8', 'result': '1-0'}, 79 | {'sub-fen': '8/2q5/8/8/8/8/8/R6R'}]}, 80 | 'count': 8, 'matches': [{'ofs': 195418, 'ply': [0, 42]}, {'ofs': 323394, 'ply': [0, 8]}]}, 81 | 82 | {'q': {'sequence': [{'sub-fen': 'r1bqkb1r/pppp1ppp/2n2n2/1B2p3/' 83 | '4P3/2N2N2/PPPP1PPP/R1BQK2R'}, 84 | {'sub-fen': '8/8/8/8/2B5/8/8/8'}, 85 | {'sub-fen': '8/8/8/8/8/5B2/8/8'}]}, 86 | 'count': 2, 'matches': [{'ofs': 19722, 'ply': [7, 15, 21]}, {'ofs': 21321, 'ply': [7, 15, 21]}]}, 87 | 88 | {'q': {'streak': [{'sub-fen': 'r1bqkb1r/pppp1ppp/2n2n2/1B2p3/4P3/2N2N2/PPPP1PPP/R1BQK2R'}, 89 | {'result': '0-1'}, {'result': '0-1'}]}, 90 | 'count': 2 , 'matches': [{'ofs': 19722, 'ply': [7, 8, 9]}, {'ofs': 21321, 'ply': [7, 8, 9]}]}, 91 | 92 | {'q': {'sequence': [{'sub-fen': 'rnbqkb1r/pp1p1ppp/4pn2/2pP4/2P5/2N5/PP2PPPP/R1BQKBNR'}, 93 | {'streak': [{'white-move': 'e5'}, {'black-move': 'dxe5'}, {'white-move': 'f5'}]}, 94 | {'white-move': 'Ne4'}]}, 95 | 'count': 1 , 'matches': [{'ofs': 0, 'ply': [7, 36, 37, 38, 42]}]}, 96 | 97 | {'q': {'sequence': [{'sub-fen': 'rnbqkb1r/pp1p1ppp/4pn2/2pP4/2P5/2N5/PP2PPPP/R1BQKBNR'}, 98 | {'streak': [{'white-move': 'e5'}, {'black-move': 'dxe5'}, {'white-move': 'f5'}, 99 | {'white-move': 'Ne4'}]}]}, 100 | 'count':0 , 'matches': []}, 101 | 102 | {'q': {'sequence': [{'sub-fen': 'rnbqkb1r/pp1p1ppp/4pn2/2pP4/2P5/2N5/PP2PPPP/R1BQKBNR'}, 103 | {'streak': [{'white-move': 'e5'}, {'pass': ''}, {'white-move': 'f5'}]}, 104 | {'white-move': 'Ne4'}]}, 105 | 'count': 1, 'matches': [{'ofs': 0, 'ply': [7, 36, 37, 38, 42]}]}, 106 | 107 | {'q': {'streak': [{'imbalance': 'NNvB'}, {'imbalance': 'NNvB'}, {'imbalance': 'NNvB'}]}, 108 | 'count': 4, 'matches': [{'ofs': 82982, 'ply': [39, 40, 41]}, {'ofs': 99933, 'ply': [37, 38, 39]}]}, 109 | 110 | {'q': {'streak': [{'moved': 'P', 'captured': 'Q'}, {'captured': ''}]}, 111 | 'count': 24, 'matches': [{'ofs': 19722, 'ply': [34, 35]}, {'ofs': 21321, 'ply': [34, 35]}]}, 112 | ] 113 | 114 | 115 | # Spawn scoutfish 116 | sys.stdout.write('Making index...') 117 | sys.stdout.flush() 118 | p = Scoutfish(SCOUTFISH) 119 | p.setoption('threads', 1) 120 | p.open('../pgn/famous_games.pgn') 121 | p.make() # Force rebuilding of DB index 122 | print('done') 123 | 124 | 125 | class TestSuite(unittest.TestCase): 126 | ''' Each single test will be appended here as a new method 127 | with setattr(). The methods will then be loaded and 128 | run by unittest. ''' 129 | pass 130 | 131 | 132 | def create_test(expected): 133 | ''' Defines and returns a closure function that implements 134 | a single test. ''' 135 | def test(self): 136 | result = p.scout(expected['q']) 137 | 138 | self.assertEqual(expected['count'], result['match count']) 139 | 140 | for idx, match in enumerate(expected['matches']): 141 | self.assertEqual(match['ofs'], result['matches'][idx]['ofs']) 142 | self.assertEqual(match['ply'], result['matches'][idx]['ply']) 143 | return test 144 | 145 | 146 | for cnt, expected in enumerate(QUERIES): 147 | # Create a new test method 148 | test = create_test(expected) 149 | 150 | # Change it's name to be unique in TestSuite class 151 | test.__name__ = 'test_{num:02d}'.format(num=cnt + 1) 152 | 153 | # Add test to the TestSuite class 154 | setattr(TestSuite, test.__name__, test) 155 | 156 | 157 | if __name__ == '__main__': 158 | unittest.main(verbosity=2) 159 | p.close() 160 | -------------------------------------------------------------------------------- /src/thread.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include // For std::count 22 | #include 23 | 24 | #include "movegen.h" 25 | #include "search.h" 26 | #include "thread.h" 27 | #include "uci.h" 28 | #include "syzygy/tbprobe.h" 29 | 30 | ThreadPool Threads; // Global object 31 | 32 | /// Thread constructor launches the thread and then waits until it goes to sleep 33 | /// in idle_loop(). 34 | 35 | Thread::Thread() { 36 | 37 | resetCalls = exit = false; 38 | maxPly = callsCnt = 0; 39 | tbHits = 0; 40 | history.clear(); 41 | counterMoves.clear(); 42 | idx = Threads.size(); // Start from 0 43 | 44 | std::unique_lock lk(mutex); 45 | searching = true; 46 | nativeThread = std::thread(&Thread::idle_loop, this); 47 | sleepCondition.wait(lk, [&]{ return !searching; }); 48 | } 49 | 50 | 51 | /// Thread destructor waits for thread termination before returning 52 | 53 | Thread::~Thread() { 54 | 55 | mutex.lock(); 56 | exit = true; 57 | sleepCondition.notify_one(); 58 | mutex.unlock(); 59 | nativeThread.join(); 60 | } 61 | 62 | 63 | /// Thread::wait_for_search_finished() waits on sleep condition 64 | /// until not searching 65 | 66 | void Thread::wait_for_search_finished() { 67 | 68 | std::unique_lock lk(mutex); 69 | sleepCondition.wait(lk, [&]{ return !searching; }); 70 | } 71 | 72 | 73 | /// Thread::wait() waits on sleep condition until condition is true 74 | 75 | void Thread::wait(std::atomic_bool& condition) { 76 | 77 | std::unique_lock lk(mutex); 78 | sleepCondition.wait(lk, [&]{ return bool(condition); }); 79 | } 80 | 81 | 82 | /// Thread::start_searching() wakes up the thread that will start the search 83 | 84 | void Thread::start_searching(bool resume) { 85 | 86 | std::unique_lock lk(mutex); 87 | 88 | if (!resume) 89 | searching = true; 90 | 91 | sleepCondition.notify_one(); 92 | } 93 | 94 | 95 | /// Thread::idle_loop() is where the thread is parked when it has no work to do 96 | 97 | void Thread::idle_loop() { 98 | 99 | WinProcGroup::bindThisThread(idx); 100 | 101 | while (!exit) 102 | { 103 | std::unique_lock lk(mutex); 104 | 105 | searching = false; 106 | 107 | while (!searching && !exit) 108 | { 109 | sleepCondition.notify_one(); // Wake up any waiting thread 110 | sleepCondition.wait(lk); 111 | } 112 | 113 | lk.unlock(); 114 | 115 | if (!exit) 116 | search(); 117 | } 118 | } 119 | 120 | 121 | /// ThreadPool::init() creates and launches requested threads that will go 122 | /// immediately to sleep. We cannot use a constructor because Threads is a 123 | /// static object and we need a fully initialized engine at this point due to 124 | /// allocation of Endgames in the Thread constructor. 125 | 126 | void ThreadPool::init() { 127 | 128 | push_back(new MainThread); 129 | read_uci_options(); 130 | } 131 | 132 | 133 | /// ThreadPool::exit() terminates threads before the program exits. Cannot be 134 | /// done in destructor because threads must be terminated before deleting any 135 | /// static objects while still in main(). 136 | 137 | void ThreadPool::exit() { 138 | 139 | while (size()) 140 | delete back(), pop_back(); 141 | } 142 | 143 | 144 | /// ThreadPool::read_uci_options() updates internal threads parameters from the 145 | /// corresponding UCI options and creates/destroys threads to match requested 146 | /// number. Thread objects are dynamically allocated. 147 | 148 | void ThreadPool::read_uci_options() { 149 | 150 | size_t requested = Options["Threads"]; 151 | 152 | assert(requested > 0); 153 | 154 | while (size() < requested) 155 | push_back(new Thread); 156 | 157 | while (size() > requested) 158 | delete back(), pop_back(); 159 | } 160 | 161 | 162 | /// ThreadPool::nodes_searched() returns the number of nodes searched 163 | 164 | uint64_t ThreadPool::nodes_searched() const { 165 | 166 | uint64_t nodes = 0; 167 | for (Thread* th : *this) 168 | nodes += th->rootPos.nodes_searched(); 169 | return nodes; 170 | } 171 | 172 | 173 | /// ThreadPool::tb_hits() returns the number of TB hits 174 | 175 | uint64_t ThreadPool::tb_hits() const { 176 | 177 | uint64_t hits = 0; 178 | for (Thread* th : *this) 179 | hits += th->tbHits; 180 | return hits; 181 | } 182 | 183 | 184 | /// ThreadPool::start_thinking() wakes up the main thread sleeping in idle_loop() 185 | /// and starts a new search, then returns immediately. 186 | 187 | void ThreadPool::start_thinking(Position& pos, StateListPtr& states, 188 | const Search::LimitsType& limits) { 189 | 190 | main()->wait_for_search_finished(); 191 | 192 | Search::Signals.stopOnPonderhit = Search::Signals.stop = false; 193 | Search::Limits = limits; 194 | Search::RootMoves rootMoves; 195 | 196 | if (!limits.scout.baseAddress) 197 | for (const auto& m : MoveList(pos)) 198 | if ( limits.searchmoves.empty() 199 | || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) 200 | rootMoves.push_back(Search::RootMove(m)); 201 | 202 | if (!rootMoves.empty()) 203 | Tablebases::filter_root_moves(pos, rootMoves); 204 | 205 | // After ownership transfer 'states' becomes empty, so if we stop the search 206 | // and call 'go' again without setting a new position states.get() == NULL. 207 | assert(states.get() || setupStates.get()); 208 | 209 | if (states.get()) 210 | setupStates = std::move(states); // Ownership transfer, states is now empty 211 | 212 | StateInfo tmp = setupStates->back(); 213 | 214 | for (Thread* th : Threads) 215 | { 216 | th->maxPly = 0; 217 | th->tbHits = 0; 218 | th->rootDepth = DEPTH_ZERO; 219 | th->rootMoves = rootMoves; 220 | th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); 221 | th->scout = limits.scout; 222 | } 223 | 224 | setupStates->back() = tmp; // Restore st->previous, cleared by Position::set() 225 | 226 | main()->start_searching(); 227 | } 228 | -------------------------------------------------------------------------------- /src/thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef THREAD_H_INCLUDED 22 | #define THREAD_H_INCLUDED 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "material.h" 32 | #include "movepick.h" 33 | #include "pawns.h" 34 | #include "position.h" 35 | #include "search.h" 36 | #include "thread_win32.h" 37 | 38 | 39 | /// Thread struct keeps together all the thread-related stuff. We also use 40 | /// per-thread pawn and material hash tables so that once we get a pointer to an 41 | /// entry its life time is unlimited and we don't have to care about someone 42 | /// changing the entry under our feet. 43 | 44 | class Thread { 45 | 46 | std::thread nativeThread; 47 | Mutex mutex; 48 | ConditionVariable sleepCondition; 49 | bool exit, searching; 50 | 51 | public: 52 | Thread(); 53 | virtual ~Thread(); 54 | virtual void search(); 55 | void idle_loop(); 56 | void start_searching(bool resume = false); 57 | void wait_for_search_finished(); 58 | void wait(std::atomic_bool& condition); 59 | 60 | Pawns::Table pawnsTable; 61 | Material::Table materialTable; 62 | Endgames endgames; 63 | size_t idx, PVIdx; 64 | int maxPly, callsCnt; 65 | uint64_t tbHits; 66 | 67 | Position rootPos; 68 | Search::RootMoves rootMoves; 69 | Depth rootDepth; 70 | Depth completedDepth; 71 | std::atomic_bool resetCalls; 72 | HistoryStats history; 73 | MoveStats counterMoves; 74 | FromToStats fromTo; 75 | CounterMoveHistoryStats counterMoveHistory; 76 | Scout::Data scout; 77 | }; 78 | 79 | 80 | /// MainThread is a derived class with a specific overload for the main thread 81 | 82 | struct MainThread : public Thread { 83 | virtual void search(); 84 | 85 | bool easyMovePlayed, failedLow; 86 | double bestMoveChanges; 87 | Value previousScore; 88 | }; 89 | 90 | 91 | /// ThreadPool struct handles all the threads-related stuff like init, starting, 92 | /// parking and, most importantly, launching a thread. All the access to threads 93 | /// data is done through this class. 94 | 95 | struct ThreadPool : public std::vector { 96 | 97 | void init(); // No constructor and destructor, threads rely on globals that should 98 | void exit(); // be initialized and valid during the whole thread lifetime. 99 | 100 | MainThread* main() { return static_cast(at(0)); } 101 | void start_thinking(Position&, StateListPtr&, const Search::LimitsType&); 102 | void read_uci_options(); 103 | uint64_t nodes_searched() const; 104 | uint64_t tb_hits() const; 105 | 106 | private: 107 | StateListPtr setupStates; 108 | }; 109 | 110 | extern ThreadPool Threads; 111 | 112 | #endif // #ifndef THREAD_H_INCLUDED 113 | -------------------------------------------------------------------------------- /src/thread_win32.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef THREAD_WIN32_H_INCLUDED 22 | #define THREAD_WIN32_H_INCLUDED 23 | 24 | /// STL thread library used by mingw and gcc when cross compiling for Windows 25 | /// relies on libwinpthread. Currently libwinpthread implements mutexes directly 26 | /// on top of Windows semaphores. Semaphores, being kernel objects, require kernel 27 | /// mode transition in order to lock or unlock, which is very slow compared to 28 | /// interlocked operations (about 30% slower on bench test). To work around this 29 | /// issue, we define our wrappers to the low level Win32 calls. We use critical 30 | /// sections to support Windows XP and older versions. Unfortunately, cond_wait() 31 | /// is racy between unlock() and WaitForSingleObject() but they have the same 32 | /// speed performance as the SRW locks. 33 | 34 | #include 35 | #include 36 | 37 | #if defined(_WIN32) && !defined(_MSC_VER) 38 | 39 | #ifndef NOMINMAX 40 | # define NOMINMAX // Disable macros min() and max() 41 | #endif 42 | 43 | #define WIN32_LEAN_AND_MEAN 44 | #include 45 | #undef WIN32_LEAN_AND_MEAN 46 | #undef NOMINMAX 47 | 48 | /// Mutex and ConditionVariable struct are wrappers of the low level locking 49 | /// machinery and are modeled after the corresponding C++11 classes. 50 | 51 | struct Mutex { 52 | Mutex() { InitializeCriticalSection(&cs); } 53 | ~Mutex() { DeleteCriticalSection(&cs); } 54 | void lock() { EnterCriticalSection(&cs); } 55 | void unlock() { LeaveCriticalSection(&cs); } 56 | 57 | private: 58 | CRITICAL_SECTION cs; 59 | }; 60 | 61 | typedef std::condition_variable_any ConditionVariable; 62 | 63 | #else // Default case: use STL classes 64 | 65 | typedef std::mutex Mutex; 66 | typedef std::condition_variable ConditionVariable; 67 | 68 | #endif 69 | 70 | #endif // #ifndef THREAD_WIN32_H_INCLUDED 71 | -------------------------------------------------------------------------------- /src/timeman.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "search.h" 26 | #include "timeman.h" 27 | #include "uci.h" 28 | 29 | TimeManagement Time; // Our global time management object 30 | 31 | namespace { 32 | 33 | enum TimeType { OptimumTime, MaxTime }; 34 | 35 | const int MoveHorizon = 50; // Plan time management at most this many moves ahead 36 | const double MaxRatio = 7.09; // When in trouble, we can step over reserved time with this ratio 37 | const double StealRatio = 0.35; // However we must not steal time from remaining moves over this ratio 38 | 39 | 40 | // move_importance() is a skew-logistic function based on naive statistical 41 | // analysis of "how many games are still undecided after n half-moves". Game 42 | // is considered "undecided" as long as neither side has >275cp advantage. 43 | // Data was extracted from the CCRL game database with some simple filtering criteria. 44 | 45 | double move_importance(int ply) { 46 | 47 | const double XScale = 7.64; 48 | const double XShift = 58.4; 49 | const double Skew = 0.183; 50 | 51 | return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero 52 | } 53 | 54 | template 55 | int remaining(int myTime, int movesToGo, int ply, int slowMover) { 56 | 57 | const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio); 58 | const double TStealRatio = (T == OptimumTime ? 0 : StealRatio); 59 | 60 | double moveImportance = (move_importance(ply) * slowMover) / 100; 61 | double otherMovesImportance = 0; 62 | 63 | for (int i = 1; i < movesToGo; ++i) 64 | otherMovesImportance += move_importance(ply + 2 * i); 65 | 66 | double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance); 67 | double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance); 68 | 69 | return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast 70 | } 71 | 72 | } // namespace 73 | 74 | 75 | /// init() is called at the beginning of the search and calculates the allowed 76 | /// thinking time out of the time control and current game ply. We support four 77 | /// different kinds of time controls, passed in 'limits': 78 | /// 79 | /// inc == 0 && movestogo == 0 means: x basetime [sudden death!] 80 | /// inc == 0 && movestogo != 0 means: x moves in y minutes 81 | /// inc > 0 && movestogo == 0 means: x basetime + z increment 82 | /// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment 83 | 84 | void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { 85 | 86 | int minThinkingTime = Options["Minimum Thinking Time"]; 87 | int moveOverhead = Options["Move Overhead"]; 88 | int slowMover = Options["Slow Mover"]; 89 | int npmsec = Options["nodestime"]; 90 | 91 | // If we have to play in 'nodes as time' mode, then convert from time 92 | // to nodes, and use resulting values in time management formulas. 93 | // WARNING: Given npms (nodes per millisecond) must be much lower then 94 | // the real engine speed to avoid time losses. 95 | if (npmsec) 96 | { 97 | if (!availableNodes) // Only once at game start 98 | availableNodes = npmsec * limits.time[us]; // Time is in msec 99 | 100 | // Convert from millisecs to nodes 101 | limits.time[us] = (int)availableNodes; 102 | limits.inc[us] *= npmsec; 103 | limits.npmsec = npmsec; 104 | } 105 | 106 | startTime = limits.startTime; 107 | optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime); 108 | 109 | const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon; 110 | 111 | // We calculate optimum time usage for different hypothetical "moves to go"-values 112 | // and choose the minimum of calculated search time values. Usually the greatest 113 | // hypMTG gives the minimum values. 114 | for (int hypMTG = 1; hypMTG <= MaxMTG; ++hypMTG) 115 | { 116 | // Calculate thinking time for hypothetical "moves to go"-value 117 | int hypMyTime = limits.time[us] 118 | + limits.inc[us] * (hypMTG - 1) 119 | - moveOverhead * (2 + std::min(hypMTG, 40)); 120 | 121 | hypMyTime = std::max(hypMyTime, 0); 122 | 123 | int t1 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); 124 | int t2 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); 125 | 126 | optimumTime = std::min(t1, optimumTime); 127 | maximumTime = std::min(t2, maximumTime); 128 | } 129 | 130 | if (Options["Ponder"]) 131 | optimumTime += optimumTime / 4; 132 | } 133 | -------------------------------------------------------------------------------- /src/timeman.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef TIMEMAN_H_INCLUDED 22 | #define TIMEMAN_H_INCLUDED 23 | 24 | #include "misc.h" 25 | #include "search.h" 26 | #include "thread.h" 27 | 28 | /// The TimeManagement class computes the optimal time to think depending on 29 | /// the maximum available time, the game move number and other parameters. 30 | 31 | class TimeManagement { 32 | public: 33 | void init(Search::LimitsType& limits, Color us, int ply); 34 | int optimum() const { return optimumTime; } 35 | int maximum() const { return maximumTime; } 36 | int elapsed() const { return int(Search::Limits.npmsec ? Threads.nodes_searched() : now() - startTime); } 37 | 38 | int64_t availableNodes; // When in 'nodes as time' mode 39 | 40 | private: 41 | TimePoint startTime; 42 | int optimumTime; 43 | int maximumTime; 44 | }; 45 | 46 | extern TimeManagement Time; 47 | 48 | #endif // #ifndef TIMEMAN_H_INCLUDED 49 | -------------------------------------------------------------------------------- /src/tt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include // For std::memset 22 | #include 23 | 24 | #include "bitboard.h" 25 | #include "tt.h" 26 | 27 | TranspositionTable TT; // Our global transposition table 28 | 29 | 30 | /// TranspositionTable::resize() sets the size of the transposition table, 31 | /// measured in megabytes. Transposition table consists of a power of 2 number 32 | /// of clusters and each cluster consists of ClusterSize number of TTEntry. 33 | 34 | void TranspositionTable::resize(size_t mbSize) { 35 | 36 | size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(Cluster)); 37 | 38 | if (newClusterCount == clusterCount) 39 | return; 40 | 41 | clusterCount = newClusterCount; 42 | 43 | free(mem); 44 | mem = calloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1, 1); 45 | 46 | if (!mem) 47 | { 48 | std::cerr << "Failed to allocate " << mbSize 49 | << "MB for transposition table." << std::endl; 50 | exit(EXIT_FAILURE); 51 | } 52 | 53 | table = (Cluster*)((uintptr_t(mem) + CacheLineSize - 1) & ~(CacheLineSize - 1)); 54 | } 55 | 56 | 57 | /// TranspositionTable::clear() overwrites the entire transposition table 58 | /// with zeros. It is called whenever the table is resized, or when the 59 | /// user asks the program to clear the table (from the UCI interface). 60 | 61 | void TranspositionTable::clear() { 62 | 63 | std::memset(table, 0, clusterCount * sizeof(Cluster)); 64 | } 65 | 66 | 67 | /// TranspositionTable::probe() looks up the current position in the transposition 68 | /// table. It returns true and a pointer to the TTEntry if the position is found. 69 | /// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry 70 | /// to be replaced later. The replace value of an entry is calculated as its depth 71 | /// minus 8 times its relative age. TTEntry t1 is considered more valuable than 72 | /// TTEntry t2 if its replace value is greater than that of t2. 73 | 74 | TTEntry* TranspositionTable::probe(const Key key, bool& found) const { 75 | 76 | TTEntry* const tte = first_entry(key); 77 | const uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster 78 | 79 | for (int i = 0; i < ClusterSize; ++i) 80 | if (!tte[i].key16 || tte[i].key16 == key16) 81 | { 82 | if ((tte[i].genBound8 & 0xFC) != generation8 && tte[i].key16) 83 | tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh 84 | 85 | return found = (bool)tte[i].key16, &tte[i]; 86 | } 87 | 88 | // Find an entry to be replaced according to the replacement strategy 89 | TTEntry* replace = tte; 90 | for (int i = 1; i < ClusterSize; ++i) 91 | // Due to our packed storage format for generation and its cyclic 92 | // nature we add 259 (256 is the modulus plus 3 to keep the lowest 93 | // two bound bits from affecting the result) to calculate the entry 94 | // age correctly even after generation8 overflows into the next cycle. 95 | if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2 96 | > tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2) 97 | replace = &tte[i]; 98 | 99 | return found = false, replace; 100 | } 101 | 102 | 103 | /// TranspositionTable::hashfull() returns an approximation of the hashtable 104 | /// occupation during a search. The hash is x permill full, as per UCI protocol. 105 | 106 | int TranspositionTable::hashfull() const { 107 | 108 | int cnt = 0; 109 | for (int i = 0; i < 1000 / ClusterSize; i++) 110 | { 111 | const TTEntry* tte = &table[i].entry[0]; 112 | for (int j = 0; j < ClusterSize; j++) 113 | if ((tte[j].genBound8 & 0xFC) == generation8) 114 | cnt++; 115 | } 116 | return cnt; 117 | } 118 | -------------------------------------------------------------------------------- /src/tt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef TT_H_INCLUDED 22 | #define TT_H_INCLUDED 23 | 24 | #include "misc.h" 25 | #include "types.h" 26 | 27 | /// TTEntry struct is the 10 bytes transposition table entry, defined as below: 28 | /// 29 | /// key 16 bit 30 | /// move 16 bit 31 | /// value 16 bit 32 | /// eval value 16 bit 33 | /// generation 6 bit 34 | /// bound type 2 bit 35 | /// depth 8 bit 36 | 37 | struct TTEntry { 38 | 39 | Move move() const { return (Move )move16; } 40 | Value value() const { return (Value)value16; } 41 | Value eval() const { return (Value)eval16; } 42 | Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)); } 43 | Bound bound() const { return (Bound)(genBound8 & 0x3); } 44 | 45 | void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) { 46 | 47 | assert(d / ONE_PLY * ONE_PLY == d); 48 | 49 | // Preserve any existing move for the same position 50 | if (m || (k >> 48) != key16) 51 | move16 = (uint16_t)m; 52 | 53 | // Don't overwrite more valuable entries 54 | if ( (k >> 48) != key16 55 | || d / ONE_PLY > depth8 - 4 56 | /* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */ 57 | || b == BOUND_EXACT) 58 | { 59 | key16 = (uint16_t)(k >> 48); 60 | value16 = (int16_t)v; 61 | eval16 = (int16_t)ev; 62 | genBound8 = (uint8_t)(g | b); 63 | depth8 = (int8_t)(d / ONE_PLY); 64 | } 65 | } 66 | 67 | private: 68 | friend class TranspositionTable; 69 | 70 | uint16_t key16; 71 | uint16_t move16; 72 | int16_t value16; 73 | int16_t eval16; 74 | uint8_t genBound8; 75 | int8_t depth8; 76 | }; 77 | 78 | 79 | /// A TranspositionTable consists of a power of 2 number of clusters and each 80 | /// cluster consists of ClusterSize number of TTEntry. Each non-empty entry 81 | /// contains information of exactly one position. The size of a cluster should 82 | /// divide the size of a cache line size, to ensure that clusters never cross 83 | /// cache lines. This ensures best cache performance, as the cacheline is 84 | /// prefetched, as soon as possible. 85 | 86 | class TranspositionTable { 87 | 88 | static const int CacheLineSize = 64; 89 | static const int ClusterSize = 3; 90 | 91 | struct Cluster { 92 | TTEntry entry[ClusterSize]; 93 | char padding[2]; // Align to a divisor of the cache line size 94 | }; 95 | 96 | static_assert(CacheLineSize % sizeof(Cluster) == 0, "Cluster size incorrect"); 97 | 98 | public: 99 | ~TranspositionTable() { free(mem); } 100 | void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound 101 | uint8_t generation() const { return generation8; } 102 | TTEntry* probe(const Key key, bool& found) const; 103 | int hashfull() const; 104 | void resize(size_t mbSize); 105 | void clear(); 106 | 107 | // The lowest order bits of the key are used to get the index of the cluster 108 | TTEntry* first_entry(const Key key) const { 109 | return &table[(size_t)key & (clusterCount - 1)].entry[0]; 110 | } 111 | 112 | private: 113 | size_t clusterCount; 114 | Cluster* table; 115 | void* mem; 116 | uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 117 | }; 118 | 119 | extern TranspositionTable TT; 120 | 121 | #endif // #ifndef TT_H_INCLUDED 122 | -------------------------------------------------------------------------------- /src/uci.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "evaluate.h" 26 | #include "misc.h" 27 | #include "movegen.h" 28 | #include "position.h" 29 | #include "search.h" 30 | #include "thread.h" 31 | #include "timeman.h" 32 | #include "uci.h" 33 | #include "syzygy/tbprobe.h" 34 | 35 | using namespace std; 36 | 37 | extern void benchmark(const Position& pos, istream& is); 38 | 39 | namespace Parser { 40 | void make_db(istringstream& is); 41 | } 42 | 43 | namespace { 44 | 45 | // FEN string of the initial position, normal chess 46 | const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; 47 | 48 | // A list to keep track of the position states along the setup moves (from the 49 | // start position to the position just before the search starts). Needed by 50 | // 'draw by repetition' detection. 51 | StateListPtr States(new deque(1)); 52 | 53 | 54 | // position() is called when engine receives the "position" UCI command. 55 | // The function sets up the position described in the given FEN string ("fen") 56 | // or the starting position ("startpos") and then makes the moves given in the 57 | // following move list ("moves"). 58 | 59 | void position(Position& pos, istringstream& is) { 60 | 61 | Move m; 62 | string token, fen; 63 | 64 | is >> token; 65 | 66 | if (token == "startpos") 67 | { 68 | fen = StartFEN; 69 | is >> token; // Consume "moves" token if any 70 | } 71 | else if (token == "fen") 72 | while (is >> token && token != "moves") 73 | fen += token + " "; 74 | else 75 | return; 76 | 77 | States = StateListPtr(new deque(1)); 78 | pos.set(fen, Options["UCI_Chess960"], &States->back(), Threads.main()); 79 | 80 | // Parse move list (if any) 81 | while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) 82 | { 83 | States->push_back(StateInfo()); 84 | pos.do_move(m, States->back()); 85 | } 86 | } 87 | 88 | 89 | // setoption() is called when engine receives the "setoption" UCI command. The 90 | // function updates the UCI option ("name") to the given value ("value"). 91 | 92 | void setoption(istringstream& is) { 93 | 94 | string token, name, value; 95 | 96 | is >> token; // Consume "name" token 97 | 98 | // Read option name (can contain spaces) 99 | while (is >> token && token != "value") 100 | name += string(" ", name.empty() ? 0 : 1) + token; 101 | 102 | // Read option value (can contain spaces) 103 | while (is >> token) 104 | value += string(" ", value.empty() ? 0 : 1) + token; 105 | 106 | if (Options.count(name)) 107 | Options[name] = value; 108 | else 109 | sync_cout << "No such option: " << name << sync_endl; 110 | } 111 | 112 | 113 | // go() is called when engine receives the "go" UCI command. The function sets 114 | // the thinking time and other parameters from the input string, then starts 115 | // the search. 116 | 117 | void go(Position& pos, istringstream& is) { 118 | 119 | Search::LimitsType limits; 120 | string token; 121 | 122 | limits.startTime = now(); // As early as possible! 123 | 124 | while (is >> token) 125 | if (token == "searchmoves") 126 | while (is >> token) 127 | limits.searchmoves.push_back(UCI::to_move(pos, token)); 128 | 129 | else if (token == "wtime") is >> limits.time[WHITE]; 130 | else if (token == "btime") is >> limits.time[BLACK]; 131 | else if (token == "winc") is >> limits.inc[WHITE]; 132 | else if (token == "binc") is >> limits.inc[BLACK]; 133 | else if (token == "movestogo") is >> limits.movestogo; 134 | else if (token == "depth") is >> limits.depth; 135 | else if (token == "nodes") is >> limits.nodes; 136 | else if (token == "movetime") is >> limits.movetime; 137 | else if (token == "mate") is >> limits.mate; 138 | else if (token == "infinite") limits.infinite = 1; 139 | else if (token == "ponder") limits.ponder = 1; 140 | 141 | Threads.start_thinking(pos, States, limits); 142 | } 143 | 144 | // scout() is called when engine receives the "scout" command. The function 145 | // memory-maps the Db file, sets teh correct search limits and then starts 146 | // the search. 147 | 148 | void scout(Position& pos, istringstream& is) { 149 | 150 | Search::LimitsType limits; 151 | Scout::Data& d = limits.scout; 152 | uint64_t mapping, size; 153 | void* baseAddress; 154 | string dbName; 155 | 156 | is >> dbName; 157 | 158 | if (dbName.empty()) 159 | { 160 | cerr << "Missing PGN file name..." << endl; 161 | exit(0); 162 | } 163 | 164 | mem_map(dbName.c_str(), &baseAddress, &mapping, &size); 165 | 166 | d.baseAddress = (Move*)baseAddress; 167 | d.dbMapping = mapping; 168 | d.dbSize = size / sizeof(Move); 169 | 170 | Scout::parse_query(d, is); 171 | 172 | limits.startTime = now(); 173 | 174 | Threads.start_thinking(pos, States, limits); 175 | } 176 | 177 | } // namespace 178 | 179 | 180 | /// UCI::loop() waits for a command from stdin, parses it and calls the appropriate 181 | /// function. Also intercepts EOF from stdin to ensure gracefully exiting if the 182 | /// GUI dies unexpectedly. When called with some command line arguments, e.g. to 183 | /// run 'bench', once the command is executed the function returns immediately. 184 | /// In addition to the UCI ones, also some additional debug commands are supported. 185 | 186 | void UCI::loop(int argc, char* argv[]) { 187 | 188 | Position pos; 189 | string token, cmd; 190 | 191 | pos.set(StartFEN, false, &States->back(), Threads.main()); 192 | 193 | for (int i = 1; i < argc; ++i) 194 | cmd += string(argv[i]) + " "; 195 | 196 | do { 197 | if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF 198 | cmd = "quit"; 199 | 200 | istringstream is(cmd); 201 | 202 | token.clear(); // getline() could return empty or blank line 203 | is >> skipws >> token; 204 | 205 | // The GUI sends 'ponderhit' to tell us to ponder on the same move the 206 | // opponent has played. In case Signals.stopOnPonderhit is set we are 207 | // waiting for 'ponderhit' to stop the search (for instance because we 208 | // already ran out of time), otherwise we should continue searching but 209 | // switching from pondering to normal search. 210 | if ( token == "quit" 211 | || token == "stop" 212 | || (token == "ponderhit" && Search::Signals.stopOnPonderhit)) 213 | { 214 | Search::Signals.stop = true; 215 | Threads.main()->start_searching(true); // Could be sleeping 216 | } 217 | else if (token == "ponderhit") 218 | Search::Limits.ponder = 0; // Switch to normal search 219 | 220 | else if (token == "uci") 221 | sync_cout << "id name " << engine_info(true) 222 | << "\n" << Options 223 | << "\nuciok" << sync_endl; 224 | 225 | else if (token == "ucinewgame") 226 | { 227 | Search::clear(); 228 | Tablebases::init(Options["SyzygyPath"]); 229 | Time.availableNodes = 0; 230 | } 231 | else if (token == "isready") 232 | { 233 | // This is NOT UCI compliant but is needed to sync with the UI 234 | Threads.main()->wait_for_search_finished(); 235 | sync_cout << "readyok" << sync_endl; 236 | } 237 | else if (token == "go") go(pos, is); 238 | else if (token == "position") position(pos, is); 239 | else if (token == "setoption") setoption(is); 240 | else if (token == "make") Parser::make_db(is); 241 | else if (token == "scout") scout(pos, is); 242 | 243 | // Additional custom non-UCI commands, useful for debugging 244 | else if (token == "flip") pos.flip(); 245 | else if (token == "bench") benchmark(pos, is); 246 | else if (token == "d") sync_cout << pos << sync_endl; 247 | else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; 248 | else if (token == "perft") 249 | { 250 | int depth; 251 | stringstream ss; 252 | 253 | is >> depth; 254 | ss << Options["Hash"] << " " 255 | << Options["Threads"] << " " << depth << " current perft"; 256 | 257 | benchmark(pos, ss); 258 | } 259 | else 260 | sync_cout << "Unknown command: " << cmd << sync_endl; 261 | 262 | } while (token != "quit" && argc == 1); // Passed args have one-shot behaviour 263 | 264 | Threads.main()->wait_for_search_finished(); 265 | } 266 | 267 | 268 | /// UCI::value() converts a Value to a string suitable for use with the UCI 269 | /// protocol specification: 270 | /// 271 | /// cp The score from the engine's point of view in centipawns. 272 | /// mate Mate in y moves, not plies. If the engine is getting mated 273 | /// use negative values for y. 274 | 275 | string UCI::value(Value v) { 276 | 277 | stringstream ss; 278 | 279 | if (abs(v) < VALUE_MATE - MAX_PLY) 280 | ss << "cp " << v * 100 / PawnValueEg; 281 | else 282 | ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; 283 | 284 | return ss.str(); 285 | } 286 | 287 | 288 | /// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) 289 | 290 | string UCI::square(Square s) { 291 | return string{ char('a' + file_of(s)), char('1' + rank_of(s)) }; 292 | } 293 | 294 | 295 | /// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). 296 | /// The only special case is castling, where we print in the e1g1 notation in 297 | /// normal chess mode, and in e1h1 notation in chess960 mode. Internally all 298 | /// castling moves are always encoded as 'king captures rook'. 299 | 300 | string UCI::move(Move m, bool chess960) { 301 | 302 | Square from = from_sq(m); 303 | Square to = to_sq(m); 304 | 305 | if (m == MOVE_NONE) 306 | return "(none)"; 307 | 308 | if (m == MOVE_NULL) 309 | return "0000"; 310 | 311 | if (type_of(m) == CASTLING && !chess960) 312 | to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); 313 | 314 | string move = UCI::square(from) + UCI::square(to); 315 | 316 | if (type_of(m) == PROMOTION) 317 | move += " pnbrqk"[promotion_type(m)]; 318 | 319 | return move; 320 | } 321 | 322 | 323 | /// UCI::to_move() converts a string representing a move in coordinate notation 324 | /// (g1f3, a7a8q) to the corresponding legal Move, if any. 325 | 326 | Move UCI::to_move(const Position& pos, string& str) { 327 | 328 | if (str.length() == 5) // Junior could send promotion piece in uppercase 329 | str[4] = char(tolower(str[4])); 330 | 331 | for (const auto& m : MoveList(pos)) 332 | if (str == UCI::move(m, pos.is_chess960())) 333 | return m; 334 | 335 | return MOVE_NONE; 336 | } 337 | -------------------------------------------------------------------------------- /src/uci.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef UCI_H_INCLUDED 22 | #define UCI_H_INCLUDED 23 | 24 | #include 25 | #include 26 | 27 | #include "types.h" 28 | 29 | class Position; 30 | 31 | namespace UCI { 32 | 33 | class Option; 34 | 35 | /// Custom comparator because UCI options should be case insensitive 36 | struct CaseInsensitiveLess { 37 | bool operator() (const std::string&, const std::string&) const; 38 | }; 39 | 40 | /// Our options container is actually a std::map 41 | typedef std::map OptionsMap; 42 | 43 | /// Option class implements an option as defined by UCI protocol 44 | class Option { 45 | 46 | typedef void (*OnChange)(const Option&); 47 | 48 | public: 49 | Option(OnChange = nullptr); 50 | Option(bool v, OnChange = nullptr); 51 | Option(const char* v, OnChange = nullptr); 52 | Option(int v, int minv, int maxv, OnChange = nullptr); 53 | 54 | Option& operator=(const std::string&); 55 | void operator<<(const Option&); 56 | operator int() const; 57 | operator std::string() const; 58 | 59 | private: 60 | friend std::ostream& operator<<(std::ostream&, const OptionsMap&); 61 | 62 | std::string defaultValue, currentValue, type; 63 | int min, max; 64 | size_t idx; 65 | OnChange on_change; 66 | }; 67 | 68 | void init(OptionsMap&); 69 | void loop(int argc, char* argv[]); 70 | std::string value(Value v); 71 | std::string square(Square s); 72 | std::string move(Move m, bool chess960); 73 | std::string pv(const Position& pos, Depth depth, Value alpha, Value beta); 74 | Move to_move(const Position& pos, std::string& str); 75 | 76 | } // namespace UCI 77 | 78 | extern UCI::OptionsMap Options; 79 | 80 | #endif // #ifndef UCI_H_INCLUDED 81 | -------------------------------------------------------------------------------- /src/ucioption.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) 4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 5 | Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad 6 | 7 | Stockfish is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Stockfish is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "misc.h" 26 | #include "search.h" 27 | #include "thread.h" 28 | #include "tt.h" 29 | #include "uci.h" 30 | #include "syzygy/tbprobe.h" 31 | 32 | using std::string; 33 | 34 | UCI::OptionsMap Options; // Global object 35 | 36 | namespace UCI { 37 | 38 | /// 'On change' actions, triggered by an option's value change 39 | void on_clear_hash(const Option&) { Search::clear(); } 40 | void on_hash_size(const Option& o) { TT.resize(o); } 41 | void on_logger(const Option& o) { start_logger(o); } 42 | void on_threads(const Option&) { Threads.read_uci_options(); } 43 | void on_tb_path(const Option& o) { Tablebases::init(o); } 44 | 45 | 46 | /// Our case insensitive less() function as required by UCI protocol 47 | bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { 48 | 49 | return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), 50 | [](char c1, char c2) { return tolower(c1) < tolower(c2); }); 51 | } 52 | 53 | 54 | /// init() initializes the UCI options to their hard-coded default values 55 | 56 | void init(OptionsMap& o) { 57 | 58 | const int MaxHashMB = Is64Bit ? 1024 * 1024 : 2048; 59 | 60 | o["Debug Log File"] << Option("", on_logger); 61 | o["Contempt"] << Option(0, -100, 100); 62 | o["Threads"] << Option(1, 1, 128, on_threads); 63 | o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); 64 | o["Clear Hash"] << Option(on_clear_hash); 65 | o["Ponder"] << Option(false); 66 | o["MultiPV"] << Option(1, 1, 500); 67 | o["Skill Level"] << Option(20, 0, 20); 68 | o["Move Overhead"] << Option(30, 0, 5000); 69 | o["Minimum Thinking Time"] << Option(20, 0, 5000); 70 | o["Slow Mover"] << Option(89, 10, 1000); 71 | o["nodestime"] << Option(0, 0, 10000); 72 | o["UCI_Chess960"] << Option(false); 73 | o["SyzygyPath"] << Option("", on_tb_path); 74 | o["SyzygyProbeDepth"] << Option(1, 1, 100); 75 | o["Syzygy50MoveRule"] << Option(true); 76 | o["SyzygyProbeLimit"] << Option(6, 0, 6); 77 | } 78 | 79 | 80 | /// operator<<() is used to print all the options default values in chronological 81 | /// insertion order (the idx field) and in the format defined by the UCI protocol. 82 | 83 | std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { 84 | 85 | for (size_t idx = 0; idx < om.size(); ++idx) 86 | for (const auto& it : om) 87 | if (it.second.idx == idx) 88 | { 89 | const Option& o = it.second; 90 | os << "\noption name " << it.first << " type " << o.type; 91 | 92 | if (o.type != "button") 93 | os << " default " << o.defaultValue; 94 | 95 | if (o.type == "spin") 96 | os << " min " << o.min << " max " << o.max; 97 | 98 | break; 99 | } 100 | 101 | return os; 102 | } 103 | 104 | 105 | /// Option class constructors and conversion operators 106 | 107 | Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f) 108 | { defaultValue = currentValue = v; } 109 | 110 | Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f) 111 | { defaultValue = currentValue = (v ? "true" : "false"); } 112 | 113 | Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f) 114 | {} 115 | 116 | Option::Option(int v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f) 117 | { defaultValue = currentValue = std::to_string(v); } 118 | 119 | Option::operator int() const { 120 | assert(type == "check" || type == "spin"); 121 | return (type == "spin" ? stoi(currentValue) : currentValue == "true"); 122 | } 123 | 124 | Option::operator std::string() const { 125 | assert(type == "string"); 126 | return currentValue; 127 | } 128 | 129 | 130 | /// operator<<() inits options and assigns idx in the correct printing order 131 | 132 | void Option::operator<<(const Option& o) { 133 | 134 | static size_t insert_order = 0; 135 | 136 | *this = o; 137 | idx = insert_order++; 138 | } 139 | 140 | 141 | /// operator=() updates currentValue and triggers on_change() action. It's up to 142 | /// the GUI to check for option's limits, but we could receive the new value from 143 | /// the user by console window, so let's check the bounds anyway. 144 | 145 | Option& Option::operator=(const string& v) { 146 | 147 | assert(!type.empty()); 148 | 149 | if ( (type != "button" && v.empty()) 150 | || (type == "check" && v != "true" && v != "false") 151 | || (type == "spin" && (stoi(v) < min || stoi(v) > max))) 152 | return *this; 153 | 154 | if (type != "button") 155 | currentValue = v; 156 | 157 | if (on_change) 158 | on_change(*this); 159 | 160 | return *this; 161 | } 162 | 163 | } // namespace UCI 164 | --------------------------------------------------------------------------------