Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 112 | pmbaty | 1 | /* |
| 2 | Protector -- a UCI chess engine |
||
| 3 | |||
| 4 | Copyright (C) 2009-2010 Raimund Heid (Raimund_Heid@yahoo.com) |
||
| 5 | |||
| 6 | This program 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 | This program 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 <http://www.gnu.org/licenses/>. |
||
| 18 | |||
| 19 | */ |
||
| 20 | |||
| 21 | #include <stdio.h> |
||
| 22 | #include <string.h> |
||
| 23 | #include <assert.h> |
||
| 24 | #include "matesearch.h" |
||
| 25 | #include "io.h" |
||
| 26 | #include "movegeneration.h" |
||
| 27 | #include "hash.h" |
||
| 28 | #include "coordination.h" |
||
| 29 | |||
| 30 | static int searchMate(Variation * variation, int alpha, int beta, |
||
| 31 | const int ply, const int restDepth, |
||
| 32 | PrincipalVariation * pv) |
||
| 33 | { |
||
| 34 | const int oldAlpha = alpha; |
||
| 35 | Position *position = &variation->singlePosition; |
||
| 36 | const bool check = activeKingIsSafe(position) == FALSE; |
||
| 37 | int best = VALUE_MATED; |
||
| 38 | Movelist movelist; |
||
| 39 | Hashentry *tableHit = NULL; |
||
| 40 | UINT8 hashentryFlag; |
||
| 41 | int i, historyLimit; |
||
| 42 | Move hashmove = NO_MOVE; |
||
| 43 | Move bestMove = NO_MOVE; |
||
| 44 | Move currentMove; |
||
| 45 | PrincipalVariation newPv; |
||
| 46 | |||
| 47 | newPv.length = pv->length = 0; |
||
| 48 | |||
| 49 | historyLimit = POSITION_HISTORY_OFFSET + variation->ply - |
||
| 50 | variation->singlePosition.halfMoveClock; |
||
| 51 | |||
| 52 | assert(historyLimit >= 0); |
||
| 53 | |||
| 54 | for (i = POSITION_HISTORY_OFFSET + variation->ply - 4; |
||
| 55 | i >= historyLimit; i -= 2) |
||
| 56 | { |
||
| 57 | if (variation->singlePosition.hashKey == variation->positionHistory[i]) |
||
| 58 | { |
||
| 59 | return 0; |
||
| 60 | } |
||
| 61 | } |
||
| 62 | |||
| 63 | /* Probe the transposition table */ |
||
| 64 | /* ----------------------------- */ |
||
| 65 | tableHit = getHashentry(getSharedHashtable(), |
||
| 66 | variation->singlePosition.hashKey); |
||
| 67 | |||
| 68 | if (tableHit != NULL) |
||
| 69 | { |
||
| 70 | const int importance = getHashentryImportance(tableHit); |
||
| 71 | |||
| 72 | hashmove = (Move) getHashentryMove(tableHit); |
||
| 73 | |||
| 74 | if (hashmove != NO_MOVE) |
||
| 75 | { |
||
| 76 | if (moveIsPseudoLegal(position, hashmove)) |
||
| 77 | { |
||
| 78 | assert(moveIsLegal(position, hashmove)); |
||
| 79 | |||
| 80 | appendMoveToPv(&newPv, pv, hashmove); |
||
| 81 | } |
||
| 82 | else |
||
| 83 | { |
||
| 84 | hashmove = NO_MOVE; |
||
| 85 | } |
||
| 86 | } |
||
| 87 | |||
| 88 | if (restDepth <= importance) |
||
| 89 | { /* 99% */ |
||
| 90 | const int value = getHashentryValue(tableHit); |
||
| 91 | const int hashValue = calcEffectiveValue(value, ply); |
||
| 92 | const int flag = getHashentryFlag(tableHit); |
||
| 93 | |||
| 94 | switch (flag) |
||
| 95 | { |
||
| 96 | case HASHVALUE_UPPER_LIMIT: |
||
| 97 | if (hashValue <= alpha) |
||
| 98 | { |
||
| 99 | return hashValue; |
||
| 100 | } |
||
| 101 | break; |
||
| 102 | |||
| 103 | case HASHVALUE_EXACT: |
||
| 104 | return hashValue; |
||
| 105 | |||
| 106 | case HASHVALUE_LOWER_LIMIT: |
||
| 107 | if (hashValue >= beta) |
||
| 108 | { |
||
| 109 | return hashValue; |
||
| 110 | } |
||
| 111 | break; |
||
| 112 | |||
| 113 | default:; |
||
| 114 | } |
||
| 115 | } |
||
| 116 | } |
||
| 117 | |||
| 118 | if (ply >= 2) |
||
| 119 | { |
||
| 120 | variation->plyInfo[ply].killerMove3 = |
||
| 121 | variation->plyInfo[ply - 2].killerMove1; |
||
| 122 | variation->plyInfo[ply].killerMove4 = |
||
| 123 | variation->plyInfo[ply - 2].killerMove2; |
||
| 124 | } |
||
| 125 | else |
||
| 126 | { |
||
| 127 | variation->plyInfo[ply].killerMove3 = NO_MOVE; |
||
| 128 | variation->plyInfo[ply].killerMove4 = NO_MOVE; |
||
| 129 | } |
||
| 130 | |||
| 131 | if (restDepth == 1) |
||
| 132 | { |
||
| 133 | initCheckMovelist(&movelist, position, &variation->historyValue[0]); |
||
| 134 | } |
||
| 135 | else |
||
| 136 | { |
||
| 137 | movelist.positionalGain = &(variation->positionalGain[0]); |
||
| 138 | initStandardMovelist(&movelist, &variation->singlePosition, |
||
| 139 | &variation->plyInfo[ply], |
||
| 140 | &variation->historyValue[0], hashmove, check); |
||
| 141 | } |
||
| 142 | |||
| 143 | initializePlyInfo(variation); |
||
| 144 | |||
| 145 | while ((currentMove = getNextMove(&movelist)) != NO_MOVE) |
||
| 146 | { |
||
| 147 | int value; |
||
| 148 | |||
| 149 | variation->nodes++; |
||
| 150 | |||
| 151 | if (makeMoveFast(variation, currentMove) != 0 || |
||
| 152 | passiveKingIsSafe(&variation->singlePosition) == FALSE || |
||
| 153 | (restDepth == 1 && activeKingIsSafe(&variation->singlePosition))) |
||
| 154 | { |
||
| 155 | unmakeLastMove(variation); |
||
| 156 | |||
| 157 | continue; |
||
| 158 | } |
||
| 159 | |||
| 160 | if (restDepth > 0) |
||
| 161 | { |
||
| 162 | if (restDepth == 1) |
||
| 163 | { |
||
| 164 | if (kingCanEscape(position)) |
||
| 165 | { |
||
| 166 | value = 0; |
||
| 167 | } |
||
| 168 | else |
||
| 169 | { |
||
| 170 | value = -(VALUE_MATED + ply + 1); |
||
| 171 | } |
||
| 172 | } |
||
| 173 | else |
||
| 174 | { |
||
| 175 | value = -searchMate(variation, -beta, -alpha, ply + 1, |
||
| 176 | restDepth - 1, &newPv); |
||
| 177 | } |
||
| 178 | } |
||
| 179 | else |
||
| 180 | { |
||
| 181 | value = 0; |
||
| 182 | } |
||
| 183 | |||
| 184 | unmakeLastMove(variation); |
||
| 185 | |||
| 186 | if (value > best) |
||
| 187 | { |
||
| 188 | best = value; |
||
| 189 | appendMoveToPv(&newPv, pv, currentMove); |
||
| 190 | |||
| 191 | if (best > alpha) |
||
| 192 | { |
||
| 193 | alpha = best; |
||
| 194 | bestMove = currentMove; |
||
| 195 | |||
| 196 | if (ply == 0) |
||
| 197 | { |
||
| 198 | if (variation->threadNumber == 0 && |
||
| 199 | variation->handleSearchEvent != 0) |
||
| 200 | { |
||
| 201 | getGuiSearchMutex(); |
||
| 202 | variation->handleSearchEvent(SEARCHEVENT_NEW_PV, variation); |
||
| 203 | releaseGuiSearchMutex(); |
||
| 204 | } |
||
| 205 | } |
||
| 206 | |||
| 207 | if (best >= beta) |
||
| 208 | { |
||
| 209 | goto finish; |
||
| 210 | } |
||
| 211 | } |
||
| 212 | } |
||
| 213 | } |
||
| 214 | |||
| 215 | if (best == VALUE_MATED) |
||
| 216 | { |
||
| 217 | if (check) |
||
| 218 | { |
||
| 219 | /* mate */ |
||
| 220 | |||
| 221 | best = VALUE_MATED + ply; |
||
| 222 | } |
||
| 223 | else |
||
| 224 | { |
||
| 225 | /* stalemate */ |
||
| 226 | |||
| 227 | best = variation->drawScore[position->activeColor]; |
||
| 228 | } |
||
| 229 | } |
||
| 230 | |||
| 231 | finish: |
||
| 232 | |||
| 233 | if (bestMove != NO_MOVE) |
||
| 234 | { |
||
| 235 | if (position->piece[getToSquare(bestMove)] == NO_PIECE && |
||
| 236 | getNewPiece(bestMove) == NO_PIECE && |
||
| 237 | (getToSquare(bestMove) != position->enPassantSquare || |
||
| 238 | pieceType(position->piece[getFromSquare(bestMove)]) != PAWN)) |
||
| 239 | { |
||
| 240 | Move killerMove = bestMove; |
||
| 241 | const Piece movingPiece = position->piece[getFromSquare(killerMove)]; |
||
| 242 | const int index = historyIndex(bestMove, position); |
||
| 243 | |||
| 244 | setMoveValue(&killerMove, movingPiece); |
||
| 245 | registerKillerMove(&variation->plyInfo[ply], killerMove); |
||
| 246 | |||
| 247 | variation->historyValue[index] = (UINT16) |
||
| 248 | (variation->historyValue[index] + (restDepth * restDepth)); |
||
| 249 | |||
| 250 | if (variation->historyValue[index] > HISTORY_MAX) |
||
| 251 | { |
||
| 252 | shrinkHistoryValues(variation); |
||
| 253 | } |
||
| 254 | } |
||
| 255 | } |
||
| 256 | |||
| 257 | /* Store the value in the transposition table. */ |
||
| 258 | /* ------------------------------------------- */ |
||
| 259 | if (best > oldAlpha) |
||
| 260 | { |
||
| 261 | hashentryFlag = |
||
| 262 | (best >= beta ? HASHVALUE_LOWER_LIMIT : HASHVALUE_EXACT); |
||
| 263 | } |
||
| 264 | else |
||
| 265 | { |
||
| 266 | hashentryFlag = HASHVALUE_UPPER_LIMIT; |
||
| 267 | } |
||
| 268 | |||
| 269 | setHashentry(getSharedHashtable(), position->hashKey, |
||
| 270 | calcHashtableValue(best, ply), (INT8) restDepth, |
||
| 271 | packedMove(bestMove), hashentryFlag, 0); |
||
| 272 | |||
| 273 | return best; |
||
| 274 | } |
||
| 275 | |||
| 276 | static int searchBaseMoves(Variation * variation, const int alpha, |
||
| 277 | const int beta, const int restDepth, |
||
| 278 | Movelist * solutions) |
||
| 279 | { |
||
| 280 | Position *position = &variation->singlePosition; |
||
| 281 | const bool check = activeKingIsSafe(position) == FALSE; |
||
| 282 | int best = VALUE_MATED, ply = 0; |
||
| 283 | Movelist movelist; |
||
| 284 | Move hashmove = NO_MOVE; |
||
| 285 | Move currentMove; |
||
| 286 | PrincipalVariation pv; |
||
| 287 | |||
| 288 | if (restDepth == 1) |
||
| 289 | { |
||
| 290 | initCheckMovelist(&movelist, position, &variation->historyValue[0]); |
||
| 291 | } |
||
| 292 | else |
||
| 293 | { |
||
| 294 | movelist.positionalGain = &(variation->positionalGain[0]); |
||
| 295 | initStandardMovelist(&movelist, &variation->singlePosition, |
||
| 296 | &variation->plyInfo[ply], |
||
| 297 | &variation->historyValue[0], hashmove, check); |
||
| 298 | } |
||
| 299 | |||
| 300 | initializePlyInfo(variation); |
||
| 301 | |||
| 302 | while ((currentMove = getNextMove(&movelist)) != NO_MOVE) |
||
| 303 | { |
||
| 304 | int value; |
||
| 305 | |||
| 306 | variation->nodes++; |
||
| 307 | |||
| 308 | if (makeMoveFast(variation, currentMove) != 0 || |
||
| 309 | passiveKingIsSafe(&variation->singlePosition) == FALSE || |
||
| 310 | (restDepth == 1 && activeKingIsSafe(&variation->singlePosition))) |
||
| 311 | { |
||
| 312 | unmakeLastMove(variation); |
||
| 313 | |||
| 314 | continue; |
||
| 315 | } |
||
| 316 | |||
| 317 | value = |
||
| 318 | -searchMate(variation, -beta, -alpha, ply + 1, restDepth - 1, &pv); |
||
| 319 | |||
| 320 | unmakeLastMove(variation); |
||
| 321 | |||
| 322 | if (value >= best) |
||
| 323 | { |
||
| 324 | best = variation->pv[0].score = value; |
||
| 325 | setMoveValue(¤tMove, value); |
||
| 326 | appendMoveToPv(&pv, &variation->pv[0], currentMove); |
||
| 327 | |||
| 328 | if (best > alpha) |
||
| 329 | { |
||
| 330 | variation->bestBaseMove = currentMove; |
||
| 331 | solutions->moves[solutions->numberOfMoves++] = currentMove; |
||
| 332 | |||
| 333 | if (variation->threadNumber == 0 && |
||
| 334 | variation->handleSearchEvent != 0) |
||
| 335 | { |
||
| 336 | getGuiSearchMutex(); |
||
| 337 | variation->handleSearchEvent(SEARCHEVENT_NEW_PV, variation); |
||
| 338 | releaseGuiSearchMutex(); |
||
| 339 | } |
||
| 340 | } |
||
| 341 | } |
||
| 342 | } |
||
| 343 | |||
| 344 | if (best == VALUE_MATED) |
||
| 345 | { |
||
| 346 | if (check) |
||
| 347 | { |
||
| 348 | /* mate */ |
||
| 349 | |||
| 350 | best = VALUE_MATED + ply; |
||
| 351 | } |
||
| 352 | else |
||
| 353 | { |
||
| 354 | /* stalemate */ |
||
| 355 | |||
| 356 | best = variation->drawScore[position->activeColor]; |
||
| 357 | } |
||
| 358 | } |
||
| 359 | |||
| 360 | return best; |
||
| 361 | } |
||
| 362 | |||
| 363 | void searchForMate(Variation * variation, Movelist * foundSolutions, |
||
| 364 | int numMoves) |
||
| 365 | { |
||
| 366 | int numSolutions = 0, i; |
||
| 367 | |||
| 368 | variation->startTime = getTimestamp(); |
||
| 369 | variation->startTimeProcess = getProcessTimestamp(); |
||
| 370 | variation->timestamp = variation->startTime + 1; |
||
| 371 | resetHashtable(getSharedHashtable()); |
||
| 372 | getLegalMoves(variation, foundSolutions); |
||
| 373 | variation->numberOfBaseMoves = foundSolutions->numberOfMoves; |
||
| 374 | variation->searchStatus = SEARCH_STATUS_RUNNING; |
||
| 375 | |||
| 376 | foundSolutions->numberOfMoves = 0; |
||
| 377 | resetHistoryValues(variation); |
||
| 378 | |||
| 379 | for (variation->iteration = 1; variation->iteration <= numMoves; |
||
| 380 | variation->iteration++) |
||
| 381 | { |
||
| 382 | const int restDepth = 2 * variation->iteration - 1; |
||
| 383 | const int beta = -VALUE_MATED - restDepth; |
||
| 384 | |||
| 385 | searchBaseMoves(variation, beta - 1, beta, restDepth, foundSolutions); |
||
| 386 | } |
||
| 387 | |||
| 388 | variation->finishTime = getTimestamp(); |
||
| 389 | variation->finishTimeProcess = getProcessTimestamp(); |
||
| 390 | variation->searchStatus = SEARCH_STATUS_TERMINATE; |
||
| 391 | |||
| 392 | for (i = 0; i < foundSolutions->numberOfMoves; i++) |
||
| 393 | { |
||
| 394 | if (getMoveValue(foundSolutions->moves[i]) >= |
||
| 395 | -VALUE_MATED - 2 * numMoves + 1) |
||
| 396 | { |
||
| 397 | numSolutions++; |
||
| 398 | } |
||
| 399 | } |
||
| 400 | |||
| 401 | foundSolutions->numberOfMoves = numSolutions; |
||
| 402 | |||
| 403 | getGuiSearchMutex(); |
||
| 404 | |||
| 405 | if (variation->threadNumber == 0 && |
||
| 406 | variation->handleSearchEvent != 0 && |
||
| 407 | variation->searchStatus == SEARCH_STATUS_TERMINATE) |
||
| 408 | { |
||
| 409 | variation->handleSearchEvent(SEARCHEVENT_SEARCH_FINISHED, variation); |
||
| 410 | } |
||
| 411 | |||
| 412 | releaseGuiSearchMutex(); |
||
| 413 | } |
||
| 414 | |||
| 415 | int initializeModuleMatesearch() |
||
| 416 | { |
||
| 417 | return 0; |
||
| 418 | } |
||
| 419 | |||
| 420 | int testModuleMatesearch() |
||
| 421 | { |
||
| 422 | return 0; |
||
| 423 | } |