From 29d73f4dd59554a349aa8e86e5ea65f28c912ec9 Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Mon, 24 Aug 2026 19:31:36 -0700 Subject: Started doing texel eval tuning. --- src/CLAUDE.md | 257 ++++ src/chess.h | 2 +- src/command.c | 191 ++- src/eval.c | 970 ++++++------- src/fathom/LICENSE | 24 + src/fathom/stdendian.h | 286 ++++ src/fathom/tbchess.c | 1050 +++++++++++++++ src/fathom/tbconfig.h | 150 +++ src/fathom/tbprobe.c | 2715 +++++++++++++++++++++++++++++++++++++ src/fathom/tbprobe.h | 399 ++++++ src/main.c | 10 +- src/position.bin | Bin 24 -> 0 bytes src/position.lrn | 5 - src/suite_diff.pl | 2 +- src/trial.ep_ | 3520 ------------------------------------------------ src/unix.c | 144 +- 16 files changed, 5622 insertions(+), 4103 deletions(-) create mode 100644 src/CLAUDE.md create mode 100644 src/fathom/LICENSE create mode 100644 src/fathom/stdendian.h create mode 100644 src/fathom/tbchess.c create mode 100644 src/fathom/tbconfig.h create mode 100644 src/fathom/tbprobe.c create mode 100644 src/fathom/tbprobe.h delete mode 100644 src/position.bin delete mode 100644 src/position.lrn delete mode 100644 src/trial.ep_ diff --git a/src/CLAUDE.md b/src/CLAUDE.md new file mode 100644 index 0000000..881b062 --- /dev/null +++ b/src/CLAUDE.md @@ -0,0 +1,257 @@ +# typhoon chess engine + +A chess engine by Scott Gasch (scott.gasch@gmail.com), originally started 2004. +Previously named "monsoon" -- any old references to that name mean this same +codebase. Speaks the xboard/WinBoard protocol. FreeBSD host, 64-bit. + +## Building + +`build.sh`'s OS detection (`expr $OSTYPE = "darwin"`) is broken in this shell +environment and falls through to a 32-bit `gcc`-based profile that doesn't +exist here (no `gcc`, and the resulting `.o` files are architecture-mismatched +against everything else). **Don't use build.sh as-is.** Build directly with: + +```sh +gmake -j5 GENETIC=1 PERF_COUNTERS=1 MP=1 SIXTYFOUR=1 +``` + +This uses `clang`/`clang++` (already the GNUmakefile default) and produces a +64-bit binary matching the historically-built one. After changing any header +touched by many translation units, `gmake clean` first to avoid 32/64-bit +object mismatches at link time. + +## Running / xboard protocol basics + +No command-line flags are required for interactive use; the binary drops into +an xboard-protocol REPL on stdin/stdout. Key commands used during development: + +- `force` -- stop the engine from auto-playing; you control both sides' moves. +- `setboard ` -- set the position. +- `eval` -- print `Static eval: ` (calls `Eval()` directly, no search). +- `evaldna dump` / `evaldna read ` / `evaldna write ` -- the + eval-constant import/export system (`g_EvalDNA` in eval.c). `evaldna` alone + (no subcommand) just prints usage -- you need `evaldna dump` explicitly. +- `sd ` / `st ` -- fixed depth vs. fixed time search limits. +- `go` -- search and play one move for whichever side is on move, then stop + (when paired with `force` after). Repeated `go`/`force` cycles correctly + drive a full self-play game -- the engine tracks its own position across + calls, no need to feed the opponent's move back manually. +- `script ` -- run a list of commands from a file, used for test suites + (see ECM below). Prints solved/unsolved stats at the end. +- `--dnafile ` (startup flag) or `evaldna read ` (runtime) load a + DNA file produced by tuning; `--egtbpath ` or `set EGTBPath ` + configure Syzygy tablebase lookup. + +**Default EGTB path is hardcoded** in `main.c:297` to `/zscratch/egtb`, used +whenever `--egtbpath` isn't passed. That directory currently has full 5-man +WDL+DTZ Syzygy coverage but is **missing the WDL half of its 6-man set** (only +`.rtbz`/DTZ files exist for 6-man combos) -- `init_tb()` in +`fathom/tbprobe.c:752` requires the WDL file to exist before registering a +combination at all, so every 6-man entry is silently skipped and the engine +reports "up to 5 men" even though 6-man `.rtbz` files are sitting right there. +Not a config issue; the fix is downloading the missing 6-man `.rtbw` files. + +## Testing: the ECM tactical suite + +`tests/ecm.ep_` is a 3517-line / 881-problem EPD tactical test suite. +`test.sh ` runs it and diffs against +`lastrun.log` via `suite_diff.pl`. Two things to know: + +1. **`suite_diff.pl`'s shebang (`#!/usr/bin/perl`) is wrong on this box** -- + perl lives at `/usr/local/bin/perl`. Invoke it as `perl ./suite_diff.pl + ` rather than `./suite_diff.pl`. +2. **Prefer `sd ` over `st ` for before/after eval + comparisons.** A time-based limit lets identical positions reach different + search depths across runs purely from incidental machine load (this + session saw large timing swings from unrelated processes/scrubs) -- + `sd 10` (or similar) holds search effort constant so a solve-rate + difference is actually attributable to the eval change being tested. + +Current baseline (`lastrun.log`, `st 1`, unmodified eval constants): +**606/879 solved.** A first full DNA-tuning pass against 600k TWIC positions +raised this to **622/879** (see `eval_tune/`). + +`Trace()` (used for almost all engine output, including ECM's per-problem +results) only `fflush()`s stdout, **not** the logfile -- `Log()`/`Bug()` do +flush the logfile every call, but `Trace()` doesn't. A `--logfile` file can +lag well behind actual progress while a run is in flight; it catches up fully +once the process exits. Don't trust a logfile's line count as a live +progress indicator for a running batch job. + +## Eval tuning pipeline (`eval_tune/`) + +`tune_eval_dna.py` is a Texel-style tuner: drives `typhoon` as an +xboard-protocol subprocess, fits `g_EvalDNA`'s ~1870 raw constants (reduced to +~468 after collapsing cells that share a starting value -- see caveat below) +against TWIC game outcomes via sigmoid-squared-error + coordinate descent. +`dna_diff.py` renders a human-readable, per-array diff between two `.dna` +files (with 8x8 grids for the board-shaped location tables) so you can +actually see what a tuning run changed. Position pools are cached under +`position_cache/` keyed on source-file mtime+size+filter args, since a +500k-600k position PGN scan takes ~20 minutes. + +**Two real bugs were found and fixed this session, both in `command.c`, not +in the Python tooling:** + +1. **`EvalCommand` used to malloc + fully zero (`InitializeSearcherContext`, + which memsets the whole ~75MB `SEARCHER_THREAD_CONTEXT` -- `rgPawnHash` + and `rgEvalHash` are embedded arrays inside that struct, not + separately-managed heap buffers) on every single `eval` call, then free + it.** Fixed to keep one persistent context and use the cheap + `ReInitializeSearcherContext` (just repositions + zeros counters) on + subsequent calls -- ~1000x throughput improvement for scripted eval + scoring. +2. **That persistent context's embedded eval hash was never invalidated when + new DNA was loaded**, so after the fix above, a position scored once under + any DNA would return that *same cached score forever*, silently ignoring + every later `evaldna read`. Fixed via a global `g_uDnaGeneration` counter + (bumped in `EvalDnaCommand` on successful load, checked in `EvalCommand`) + that forces exactly one full re-init per DNA change, not per position. + **If this regresses, tuning results will look like it "converged" almost + immediately with implausibly little movement -- that's the symptom, not + genuine convergence.** Verify by perturbing a few known-live parameters + and confirming the reported error actually changes. + +**Known limitation, not yet fixed:** the distinct-value grouping used to +shrink 1870 raw cells to ~468 tunable parameters ties together *any* cells +that start out numerically equal, for whatever reason -- true mirror +symmetry, an intentional-but-asymmetric design choice (e.g. +`KING_INITIAL_COUNTER_BY_LOCATION`'s rank-8 row `1,0,0,0,1,0,0,1` flags +a1/e1/h1 specifically, not a geometric mirror), or pure incidental coincidence +(e.g. `PAWN_CENTRALITY_BONUS`'s legitimate "no bonus" squares happening to +share the value `0` with unrelated off-board padding cells). The padding case +is harmless (those indices are never read during real eval) but the +coincidental-equality case is a real, unaddressed source of noise in the +parameter space, most visible on small/noisy training runs (sentinel `-1` +placeholder cells in `TRADE_PIECES` drifted to an arbitrary shared value on a +25k-position smoke test; they stayed put on the full 600k run). + +**`Engine`/`EnginePool` in `tune_eval_dna.py` must be used as a context +manager (`with Engine(...) as e:`) or have `.quit()` called explicitly.** +Each spawned engine holds a kernel SysV semaphore +(`semget(IPC_PRIVATE, ...)` in `unix.c`) for its input-dispatch loop, which is +**only released by a graceful `quit`, not by the OS when the process is +killed**. `kern.ipc.semmni` (max semaphore sets system-wide) is only 50 on +this box; a session's worth of force-killed test engines can leak past that +limit, after which *every new engine* silently falls back to a 100ms-per- +command polling path (`_WaitUntilTheresInputToRead` in `input.c:63`) instead +of the fast semaphore wait -- a ~1000x slowdown that looks exactly like "the +machine is under heavy load" and cost significant debugging time before the +real cause (`ipcs -s`, then `ipcrm -s ` for each leaked one) was found. + +## Automated eval-tuning loop (`eval_tune/cycle.sh`) + +A later session built a full wash/rinse/repeat pipeline on top of the manual +tuning tools above, so a cycle goes baseline -> tune -> head-to-head gate -> +bake-in-if-better, repeatable indefinitely against a growing game pool. + +- **`filter_pgn.py --min-elo 2400 --min-ply 40`** -- fast + text-only pre-filter (no python-chess board replay, so it runs in ~100s + over a multi-GB pool) that drops short/low-rated games before they ever + reach `tune_eval_dna.py`'s slower, board-replay-based filtering. Confirmed + most of a raw TWIC pool's mass is short draws/low-rated games: filtering + the full merged `twic.pgn` (4,099,094 games) down to >=2400 Elo / >40 ply + kept 642,268 games (556MB) -- that's `twic_filtered.pgn`, the pool actually + used for tuning and match play now. +- **`match_play.py --pgn --games N --sd D`** + -- head-to-head gate match, N games (games/2 unique openings, each played + twice with colors swapped, so opening luck cancels), fixed search depth + (not clock time, same sd-over-st reasoning as ECM below). Prints + `CANDIDATE_SCORE=... LOWER95=...` -- **gate on `LOWER95 >= 0.5`, not the raw + score.** Search is non-deterministic (MP=1 multithreaded), confirmed live: + two engines loaded with *identical* DNA still split games unevenly at + small N, so a bare score >=0.5 is not evidence of a real improvement, only + a 95%-confidence lower bound that still clears break-even is. + Three real bugs were found and fixed getting this to give sane numbers: + 1. **The opening book was still live**, so early moves in every game + were book lookups that never touched eval at all -- defeats the + purpose of a match meant to compare eval quality. Fixed by pointing + `book name` at a nonexistent file each game; `root.c`'s + `g_uBookProbeFailures >= 3` gate then disables book probing for the + rest of that game after 3 cheap failed opens. + 2. **Engines were spawned with no `--hash`/`--cpus` at all**, so every + game ran on `main.c`'s memset-zero default (a 65536-entry hash table) + instead of the 256m used everywhere else in this project's own + testing -- worse move ordering, much more re-search in complex + positions. Fixed by passing `--hash 256m --cpus 1` explicitly (added + an `extra_args` param to `tune_eval_dna.py`'s `Engine` class for + this). + 3. **Every engine process also defaulted to a *shared* `typhoon.log`** + in cwd (same memset-zero default). With `--workers N` running + `2*N` concurrent engines, they were all racing on + `BackupFile()`-then-`fopen(wb+)` at startup and `fflush()`ing that + one file on every `Log()`/`Bug()` call -- real lock contention, + observed live as unexplained match slowdown. Fixed with + `--logfile -`, which `main.c` special-cases to skip file logging + entirely (no rename race, no fopen, no fflush). + 4. Separately (not a match_play.py bug but cost real wall-clock time + chasing it as one): `sample_openings()`'s original implementation did + a sequential `chess.pgn.read_game()` scan from byte 0 of the pool + file to pick even a couple of random openings -- minutes of wasted + I/O/parsing on a 556MB pool. Rewritten to seek to random byte + offsets and parse just the one game found there, cached the same way + as `tune_eval_dna.py`'s `position_cache/` (keyed on pool identity + + params) under `opening_cache/`. + **If a match run seems to hang or take far longer than a lone `sd N` + search should:** check for leftover engine processes from a previous + killed/interrupted run first (`ps aux | grep typhoon`) before assuming a + new bug -- a stray process from an earlier debug session silently eating a + full CPU core was mistaken for a pipeline bug for a while before being + found and killed. +- **`bake_dna.py --eval-c eval.c --out eval.c`** -- writes a + winning candidate's values into `eval.c`'s array initializers *in place*, + preserving all formatting/comments/8x8 grid layout (regex-replaces only + the numeric literals found outside comments, in `g_EvalDNA[]` order, so a + resulting `git diff` shows only the numbers that actually changed). + Round-trip verified: dumping the currently-compiled-in DNA and baking it + right back produces a byte-identical `eval.c`. **Positional, not + name-matched** -- a `.dna` file only works against the exact `eval.c` + revision it was tuned against; a line-count mismatch against the current + `g_EvalDNA[]` aborts loudly rather than silently misassigning values. +- **`dna_trend.py cycle1.dna cycle2.dna ... [--cycles-dir eval_tune/cycles]`** + -- across a chronological sequence of *kept* candidates, flags whether + each raw DNA cell is trending (every step moves it the same direction -- + real signal) or flip-flopping (moves a lot, nets out near zero -- chasing + sampling noise in that cycle's position batch). Worth running every few + kept cycles; a consistently flip-flopping cell is a candidate to exclude + from future tuning passes rather than let it keep eating budget on noise. +- **`cycle.sh [pgn] [n_games] [workers] [max_positions] [max_passes] [sd_depth]`** + -- wires all of the above into one cycle: dump baseline -> tune -> gate + match -> bake-in-and-rebuild if `LOWER95 >= 0.5`, else discard and leave + `eval.c` untouched. **Never runs `git commit`** -- a win leaves `eval.c` + modified-but-uncommitted for manual review, by design. + `sd_depth` defaults to 10; a dry run at that depth was observed still + running after 15+ minutes on one game before the bugs above were found -- + use a shallow depth (4-6) for pipeline-wiring smoke tests, not 10. +- **`evaldna write ` refuses to overwrite an existing file** (silent + `Error writing dna file.` with no explanation of why) -- `WriteEvalDNA` in + eval.c does `if (SystemDoesFileExist(...)) goto end;` rather than + clobbering. Delete the target first when re-dumping a baseline. +- **Watch for apples-to-oranges ECM comparisons across depths/time + controls.** A baseline solved-count recorded at `st 1` is not a fair + comparison point against a candidate scored at `sd 20` -- rescoring the + baseline at the *same* fixed depth as the candidate revealed a real gap + (711 vs 707 at `sd 20`, both far below the apparent 606-vs-711 jump you'd + get comparing against the old `st 1` number) that was noise-level, not the + large improvement it first looked like. Always diff ECM numbers taken at + the same `sd` depth. + +## Environment notes + +- Shared, multi-user FreeBSD box with genuinely variable load (other Claude + Code sessions, a `bhyve` VM, periodic `zpool scrub` on both `zroot` and + `zscratch`). Don't trust a single throughput measurement without a + back-to-back comparison under the same conditions; this session saw the + same benchmark vary by 10x+ run to run for reasons that turned out to be + the semaphore leak above, not genuine machine contention. +- `/zscratch/egtb` and `/usr/home/scott/typhoon/pgn/twic.pgn` are the two + large external resources this codebase's tooling depends on. `twic.pgn` + was originally 554,135 games (up through TWIC issue 608, fetched by the + long-dead `pgn/twic.pl`); a later session fetched all subsequent issues + (920-1658) directly from theweekinchess.com (its WAF blocks bare `wget`, + needs a real browser `User-Agent` + `Accept` header via `curl`) and merged + them in, growing it to 4,099,094 games (3.5GB). Note issues 609-919 are + still a gap (fetched range starts at 920) if completeness ever matters. + `pgn/twic_filtered.pgn` (642,268 games, 556MB, >=2400 Elo / >40 ply, via + `filter_pgn.py`) is the pool actually used for tuning/match-play now, not + the raw file. diff --git a/src/chess.h b/src/chess.h index 562aca8..245a3b0 100755 --- a/src/chess.h +++ b/src/chess.h @@ -1498,7 +1498,7 @@ UtilPanic(ULONG uPanicCode, // // main.c // -#define LOGFILE_NAME "typhoon.log" +#define LOGFILE_NAME "/usr/local/tmp/typhoon.log" extern FILE *g_pfLogfile; void diff --git a/src/command.c b/src/command.c index d528994..4f81b10 100755 --- a/src/command.c +++ b/src/command.c @@ -32,6 +32,11 @@ Revision History: #define MAX_ARGS 32 int g_eModeBeforeAnalyze = -1; +// Bumped by EvalDnaCommand on every successful `evaldna read`/`load`; +// EvalCommand compares against this to know its persistent eval-hash +// cache needs clearing (see EvalCommand for details). +ULONG g_uDnaGeneration = 0; + typedef void (COMMAND_PARSER_FUNCTION)(CHAR *szInput, ULONG argc, CHAR *argv[], @@ -497,19 +502,175 @@ Return value: **/ { - SEARCHER_THREAD_CONTEXT *ctx; + // rgPawnHash/rgEvalHash are embedded arrays inside + // SEARCHER_THREAD_CONTEXT (tens of MB), so allocating fresh + + // InitializeSearcherContext's full memset on every call here was + // paying that cost per invocation. Keep one persistent context + // for the life of the process and use the cheap + // ReInitializeSearcherContext (memmove position + zero counters + // only) on every call after the first, same pattern the real + // search path uses across moves. + // + // CORRECTNESS: ReInitializeSearcherContext does NOT clear + // ctx->rgEvalHash, so a stale score cached under previously-loaded + // DNA constants would otherwise be returned forever for any + // position hit more than once -- silently ignoring every + // subsequent `evaldna read`. g_uDnaGeneration (bumped by + // EvalDnaCommand on a successful load) is compared here so a DNA + // change forces exactly one full re-init (i.e. one eval-hash + // clear per reload, not per position) instead of a stale hit. + static SEARCHER_THREAD_CONTEXT *ctx = NULL; + static ULONG uLastSeenDnaGeneration = (ULONG)-1; SCORE i; - ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); - if (NULL != ctx) + if (NULL == ctx) { + ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); + if (NULL == ctx) + { + Trace("Out of memory.\n"); + return; + } InitializeSearcherContext(pos, ctx); - i = Eval(ctx, -INFINITY, +INFINITY); - Trace("Static eval: %s\n", ScoreToString(i)); - SystemFreeMemory(ctx); - } else { - Trace("Out of memory.\n"); + uLastSeenDnaGeneration = g_uDnaGeneration; + } + else if (uLastSeenDnaGeneration != g_uDnaGeneration) + { + InitializeSearcherContext(pos, ctx); + uLastSeenDnaGeneration = g_uDnaGeneration; + } + else + { + ReInitializeSearcherContext(pos, ctx); + } + i = Eval(ctx, -INFINITY, +INFINITY); + Trace("Static eval: %s\n", ScoreToString(i)); +} + + +COMMAND(QSearchCommand) +/** + +Routine description: + + This function implements the 'qsearch' engine command. + + Usage: + + qsearch + + This command takes no arguments and causes the engine to run + a quiescence search on the current board position and print + the resulting score. Unlike `eval` (a static evaluation with + no search at all), this resolves captures/checks/promotions + first, the same way the real search does immediately before + calling Eval() -- so it's a much better proxy for "is this + position actually quiet" than comparing SEE-based capture + analysis to a static score. + +Parameters: + + The COMMAND macro hides four arguments from the input parser: + + CHAR *szInput : the full line of input + ULONG argc : number of argument chunks + CHAR *argv[] : array of ptrs to each argument chunk + POSITION *pos : a POSITION pointer to operate on + +Return value: + + void + +**/ +{ + // Same persistent-context + DNA-generation-invalidation pattern as + // EvalCommand (see its comment for the full rationale) -- this + // command is meant to be called at corpus scale from the eval + // tuner, so paying a fresh ~75MB memset per call would be the + // same 1000x throughput hit that command had before it was fixed. + static SEARCHER_THREAD_CONTEXT *ctx = NULL; + static ULONG uLastSeenDnaGeneration = (ULONG)-1; + CUMULATIVE_SEARCH_FLAGS *pf; + PLY_INFO *pi; + SCORE i; + + if (NULL == ctx) + { + ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); + if (NULL == ctx) + { + Trace("Out of memory.\n"); + return; + } + InitializeSearcherContext(pos, ctx); + uLastSeenDnaGeneration = g_uDnaGeneration; + } + else if (uLastSeenDnaGeneration != g_uDnaGeneration) + { + InitializeSearcherContext(pos, ctx); + uLastSeenDnaGeneration = g_uDnaGeneration; + } + else + { + ReInitializeSearcherContext(pos, ctx); + } + + // QSearch is normally only ever entered from Search() one ply + // below the position it's evaluating (see search.c's "the only + // place Qsearch is entered" comment) -- it reads (pi-1)->mv to + // learn whether the side to move just got checked into this + // position, and asserts ctx->uPly > 0. To call it directly on + // the root position, synthesize that one piece of ply-0 state: + // a fake "previous move" whose only job is to carry the correct + // checking-move flag, so QSearch's in-check handling matches the + // position's actual, real InCheck() status instead of silently + // taking the no-standpat path incorrectly for a position that IS + // in check (or vice versa). + ctx->uPly = 1; + memset(&ctx->sPlyInfo[0].mv, 0, sizeof(MOVE)); + if (TRUE == InCheck(&ctx->sPosition, ctx->sPosition.uToMove)) + { + ctx->sPlyInfo[0].mv.uMove |= 0x80000000; + } + + pf = &ctx->sSearchFlags; + pi = &ctx->sPlyInfo[ctx->uPly]; + pf->fCouldStandPat[BLACK] = pf->fCouldStandPat[WHITE] = FALSE; + pf->uQsearchNodes = pf->uQsearchDepth = 0; + pf->uQsearchCheckDepth = QPLIES_OF_NON_CAPTURE_CHECKS; + pi->fInQsearch = TRUE; + + // QSearch's recursive move loop checks WE_SHOULD_STOP_SEARCHING, + // which reads the process-global g_MoveTimer -- normally set up + // by SetMoveTimerForSearch() at the start of every real search. + // This command is a standalone call outside that path, so without + // this g_MoveTimer is whatever state a prior real search left it + // in (e.g. an already-expired hard limit). Save/restore around + // the call so a `qsearch` invoked between real searches (e.g. + // interactive use after `go`) doesn't leave the timer in a + // "think forever" state for whatever search comes next. + // + // g_uHardExtendLimit is a second process-global with the same + // problem, and the one actually responsible for a bogus +MATE + // score on every call before this fix: CommonSearchInit's + // "uQsearchDepth >= g_uHardExtendLimit" too-deep check (see its + // comment) fires immediately when this is left at its zero- + // initialized default, since it's normally only ever set once + // per real search, inside RootSearch (g_uIterateDepth * 4) -- + // a path this standalone command never runs through. Set it + // generously (MAX_PLY_PER_SEARCH; qsearch's own capture-only move + // generation makes runaway recursion a non-issue) for the + // duration of the call, same save/restore reasoning as the timer. + { + MOVE_TIMER SavedMoveTimer = g_MoveTimer; + ULONG uSavedHardExtendLimit = g_uHardExtendLimit; + SetMoveTimerToThinkForever(); + g_uHardExtendLimit = MAX_PLY_PER_SEARCH; + i = QSearch(ctx, -INFINITY, +INFINITY); + g_MoveTimer = SavedMoveTimer; + g_uHardExtendLimit = uSavedHardExtendLimit; } + Trace("Qsearch score: %s\n", ScoreToString(i)); } @@ -568,10 +729,16 @@ Return value: Trace("Error (missing argument)\n"); return; } - if (!ReadEvalDNA(argv[2])) + if (!ReadEvalDNA(argv[2])) { Trace("Error reading dna file.\n"); } else { + // EvalCommand's persistent SEARCHER_THREAD_CONTEXT keeps + // its embedded eval hash alive across calls for speed; a + // DNA reload must invalidate stale cached scores from the + // old constants, which this generation bump signals it + // to do (see EvalCommand for the actual clear). + g_uDnaGeneration++; Trace("Loaded dna file \"%s\"\n", argv[2]); p = ExportEvalDNA(); Log("(New) dna: %s\n", p); @@ -2188,6 +2355,12 @@ COMMAND_PARSER_ENTRY g_ParserTable[] = TRUE, FALSE, "Learn a set of PSQT settings from a PGN file" }, + { "qsearch", + QSearchCommand, + FALSE, + FALSE, + FALSE, + "Run a quiescence search on the current position" }, { "quit", QuitCommand, TRUE, diff --git a/src/eval.c b/src/eval.c index 48a21e0..92d1edc 100755 --- a/src/eval.c +++ b/src/eval.c @@ -17,7 +17,7 @@ Revision History: $Id: eval.c 357 2008-07-03 16:18:11Z scott $ -**/ +**/ #include "chess.h" @@ -38,16 +38,16 @@ const int g_iBehind[2] = { -1, +1 }; // static SCORE TRADE_PIECES[3][17] = {// 0 1 2 3 4 5 6 7 8 | 9..15 -- down piece count - { -1, 90, 81, 73, 67, 62, 57, 52, 50, 50,50,50,50,50,50,50,-1}, - { -1, 125, 117, 109, 100, 91, 84, 78, 71, 66,66,66,66,66,66,66,-1}, - { -1, 150, 132, 124, 116, 109, 102, 94, 87, 77,77,77,77,77,77,77,-1}, + { -1, 90, 89, 89, 59, 46, 25, 4, 50, 50,50,50,50,50,50,50,-1}, + { -1, 125, 125, 109, 132, 123, 100, 78, 71, 66,66,66,66,66,66,66,-1}, + { -1, 158, 180, 148, 92, 109, 70, 46, 87, 77,77,77,77,77,77,77,-1}, }; static SCORE DONT_TRADE_PAWNS[3][9] = {// 0 1 2 3 4 5 6 7 8 -- up pawn count - { -43, -15, 0, +3, +6, +10, +15, +21, +28 }, // 0 - { -63, -25, 0, +4, +9, +14, +19, +25, +32 }, // 1 - { -10, 0, 0, +7, +15, +20, +24, +29, +36 }, // 2 + { -59, -63, 0, +3, +14, +18, +-9, +-19, +-20 }, // 0 + { -31, 7, 0, +52, +41, +14, +19, +41, +32 }, // 1 + { -58, 0, 0, +47, +-9, +4, +-24, +-19, +44 }, // 2 }; static ULONG REDUCED_MATERIAL_DOWN_SCALER[32] = @@ -56,7 +56,7 @@ static ULONG REDUCED_MATERIAL_DOWN_SCALER[32] = 0, 0, 0, 0, 0, 0, 0, 0, // Rm 3m/Q 2R R2m 4m/Qm 2Rm R3m/QR Q2m - 0, 1, 1, 1, 1, 1, 2, 3, + 0, 1, 1, 1, 1, 1, 10, 11, // 2R2m R4m/QRm Q3m 2R3m/ QR2m Q4m 2R4m/ QR3m // Q2R Q2Rm @@ -72,30 +72,30 @@ static ULONG REDUCED_MATERIAL_UP_SCALER[32] = 8, 8, 8, 8, 8, 8, 8, 8, // Rm 3m/Q 2R R2m 4m/Qm 2Rm R3m/QR Q2m - 8, 7, 7, 7, 7, 6, 6, 5, + 8, 15, 15, 15, 15, 6, 6, 5, // 2R2m R4m/QRm Q3m 2R3m/ QR2m Q4m 2R4m/ QR3m // Q2R Q2Rm - 4, 3, 3, 3, 2, 2, 2, 1, + 12, 11, 11, 11, 10, 10, 10, 9, // na Q2R2m QR4m na Q2R3m na na full - 1, 1, 0, 0, 0, 0, 0, 0, + 9, 9, 0, 0, 0, 0, 0, 0, }; static ULONG PASSER_MATERIAL_UP_SCALER[32] = { // none na na m na R 2m na - 8, 8, 8, 8, 8, 8, 7, 7, + 16, 16, 16, 16, 16, 16, 15, 15, // Rm 3m/Q 2R R2m 4m/Qm 2Rm R3m/QR Q2m - 7, 6, 7, 5, 4, 5, 4, 3, + 15, 14, 15, 13, 12, 13, 12, 11, // 2R2m R4m/QRm Q3m 2R3m/ QR2m Q4m 2R4m/ QR3m // Q2R Q2Rm - 2, 2, 1, 1, 0, 0, 0, 0, + 2, 2, 9, 9, -8, -8, -8, -8, // na Q2R2m QR4m na Q2R3m na na full - 0, 0, 0, 0, 0, 0, 0, 0, + -8, -8, -8, -8, -8, -8, -8, -8, }; COOR QUEENING_RANK[2] = { 1, 8 }; @@ -119,42 +119,42 @@ COOR JUMPING_RANK[2] = { 7, 2 }; // --Larry Kaufman, IM // "Evaluation of Material Imbalance" // -static SCORE PAWN_CENTRALITY_BONUS[128] = +static SCORE PAWN_CENTRALITY_BONUS[128] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0, - -8, 0, 0, 0, 0, 0, 0, -8, 0,0,0,0,0,0,0,0, - -8, 0, 5, 5, 5, 5, 0, -8, 0,0,0,0,0,0,0,0, - -8, 0, 5, 9, 9, 5, 0, -8, 0,0,0,0,0,0,0,0, + 8, 8, 8, 8, 8, 8, 8, 8, 8,8,8,8,8,8,8,8, + -8, 8, 8, 8, 8, 8, 8, -8, 8,8,8,8,8,8,8,8, + -8, 8, -3, -3, -3, -3, 8, -8, 8,8,8,8,8,8,8,8, + -8, 8, -3, 17, 17, -3, 8, -8, 8,8,8,8,8,8,8,8, // ------------------------------------------------------------------- - -8, 0, 5, 9, 9, 5, 0, -8, 0,0,0,0,0,0,0,0, - -8, 0, 5, 5, 5, 5, 0, -8, 0,0,0,0,0,0,0,0, - -8, 0, 0, 0, 0, 0, 0, -8, 0,0,0,0,0,0,0,0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0 + -8, 8, -3, 17, 17, -3, 8, -8, 8,8,8,8,8,8,8,8, + -8, 8, -3, -3, -3, -3, 8, -8, 8,8,8,8,8,8,8,8, + -8, 8, 8, 8, 8, 8, 8, -8, 8,8,8,8,8,8,8,8, + 8, 8, 8, 8, 8, 8, 8, 8, 8,8,8,8,8,8,8,8 }; -static SCORE BACKWARD_SHIELDED_BY_LOCATION[128] = +static SCORE BACKWARD_SHIELDED_BY_LOCATION[128] = { +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0, - -7, -5, -5, -7, -7, -5, -5, -7, 0,0,0,0,0,0,0,0, - -5, -4, -4, -5, -5, -4, -4, -5, 0,0,0,0,0,0,0,0, - -4, -3, -3, -4, -4, -3, -3, -4, 0,0,0,0,0,0,0,0, + 17, 3, 3, 17, 17, 3, 3, 17, 0,0,0,0,0,0,0,0, + 3, 12, 12, 3, 3, 12, 12, 3, 0,0,0,0,0,0,0,0, + 12, -3, -3, 12, 12, -3, -3, 12, 0,0,0,0,0,0,0,0, // ------------------------------------------------------------------- - -4, -3, -3, -4, -4, -3, -3, -4, 0,0,0,0,0,0,0,0, - -5, -4, -4, -5, -5, -4, -4, -5, 0,0,0,0,0,0,0,0, - -7, -5, -5, -7, -7, -5, -5, -7, 0,0,0,0,0,0,0,0, + 12, -3, -3, 12, 12, -3, -3, 12, 0,0,0,0,0,0,0,0, + 3, 12, 12, 3, 3, 12, 12, 3, 0,0,0,0,0,0,0,0, + 17, 3, 3, 17, 17, 3, 3, 17, 0,0,0,0,0,0,0,0, +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0 }; -static SCORE BACKWARD_EXPOSED_BY_LOCATION[128] = +static SCORE BACKWARD_EXPOSED_BY_LOCATION[128] = { +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0, - -12, -9, -9, -11, -11, -9, -9, -12, 0,0,0,0,0,0,0,0, - -9, -7, -7, -9, -9, -7, -7, -9, 0,0,0,0,0,0,0,0, - -7, -6, -6, -7, -7, -6, -6, -7, 0,0,0,0,0,0,0,0, + -12, 7, 7, -27, -27, 7, 7, -12, 0,0,0,0,0,0,0,0, + 7, 1, 1, 7, 7, 1, 1, 7, 0,0,0,0,0,0,0,0, + 1, -6, -6, 1, 1, -6, -6, 1, 0,0,0,0,0,0,0,0, // ------------------------------------------------------------------- - -7, -6, -6, -7, -7, -6, -6, -7, 0,0,0,0,0,0,0,0, - -9, -7, -7, -9, -9, -7, -7, -9, 0,0,0,0,0,0,0,0, - -12, -9, -9, -11, -11, -9, -9, -12, 0,0,0,0,0,0,0,0, + 1, -6, -6, 1, 1, -6, -6, 1, 0,0,0,0,0,0,0,0, + 7, 1, 1, 7, 7, 1, 1, 7, 0,0,0,0,0,0,0,0, + -12, 7, 7, -27, -27, 7, 7, -12, 0,0,0,0,0,0,0,0, +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0 }; @@ -176,99 +176,99 @@ static SCORE BACKWARD_EXPOSED_BY_LOCATION[128] = // are the one with the doubled pawns." // --Larry Kaufman, IM // "All About Doubled Pawns" -// +// // Note: indexed by number of files with 1+ pawn on them. -static SCORE DOUBLED_PAWN_PENALTY_BY_COUNT[4][9] = +static SCORE DOUBLED_PAWN_PENALTY_BY_COUNT[4][9] = { // ------------ count of doubled+ pawns ------------ {// 0 1 2 3 4 5 6 7 8 : no majors alive - +0, -32, -65, -99, -134, -170, -207, -222, -250 + +0, -32, -41, -91, -102, -170, -207, -222, -250 }, {// : 1 rook - +0, -23, -47, -76, -108, -144, -184, -200, -216 + +0, -7, 1, -76, -76, -120, -184, -200, -216 }, {// : 2 rooks or 1 queen - +0, -13, -23, -34, -46, -64, -86, -111, -138 + +0, -21, -7, -18, -30, -48, -78, -111, -138 }, {// : all majors alive - +0, -7, -13, -25, -39, -55, -73, -95, -121 + +0, -7, -21, -25, -31, -47, -57, -95, -121 }, }; -static SCORE ISOLATED_PAWN_PENALTY_BY_COUNT[9] = +static SCORE ISOLATED_PAWN_PENALTY_BY_COUNT[9] = {// 0 1 2 3 4 5 6 7 8 - +0, -3, -7, -9, -16, -35, -50, -85, -100 + +16, 5, -7, -33, -32, -19, -34, -85, -100 }; static SCORE ISOLATED_PAWN_BY_PAWNFILE[9] = { - 0, -7, -8, -9, -10, -10, -9, -8, -7 + 0, 1, -24, -9, -18, -18, -9, -24, 1 }; static SCORE ISOLATED_EXPOSED_PAWN = -5; -static SCORE ISOLATED_DOUBLED_PAWN = -11; +static SCORE ISOLATED_DOUBLED_PAWN = 13; // // Note: -25% to -33% if the enemy occupies or controls the next sq. // -static SCORE PASSER_BY_RANK[2][9] = +static SCORE PASSER_BY_RANK[2][9] = {// 0 1 2 3 4 5 6 7 8 - { +0, +0,+162,+111, +62, +36, +18, +13, +0 }, // black - { +0, +0, +13, +18, +36, +62,+111,+162, +0 } // white + { +0, +0,+194,+135, +54, +20, +-6, +-3, +0 }, // black + { +0, +0, +-3, +-6, +20, +54,+135,+194, +0 } // white }; -static SCORE CANDIDATE_PASSER_BY_RANK[2][9] = +static SCORE CANDIDATE_PASSER_BY_RANK[2][9] = {// 0 1 2 3 4 5 6 7 8 - { +0, +0, +0, +48, +34, +22, +13, +9, +0 }, // black - { +0, +0, +9, +13, +22, +34, +48, +0, +0 } // white + { +0, +0, +0, +48, +42, +14, +21, +17, +0 }, // black + { +0, +0, +17, +21, +14, +42, +48, +0, +0 } // white }; // Note: x2 -static SCORE CONNECTED_PASSERS_BY_RANK[2][9] = +static SCORE CONNECTED_PASSERS_BY_RANK[2][9] = {// 0 1 2 3 4 5 6 7 8 - { +0, +0, +96, +74, +48, +21, +10, +5, +0 }, // black - { +0, +0, +5, +10, +21, +48, +74, +96, +0 } // white + { +0, +0, +80, +58, +56, +29, +10, +21, +0 }, // black + { +0, +0, +21, +10, +29, +56, +58, +80, +0 } // white }; -static SCORE SUPPORTED_PASSER_BY_RANK[2][9] = +static SCORE SUPPORTED_PASSER_BY_RANK[2][9] = {// 0 1 2 3 4 5 6 7 8 - { +0, +0, +60, +40, +13, +6, +3, +1, +0 }, // black - { +0, +0, +1, +3, +6, +13, +40, +60, +0 } // white + { +0, +0, +92, +80, +21, +6, +11, +1, +0 }, // black + { +0, +0, +1, +11, +6, +21, +80, +92, +0 } // white }; static SCORE OUTSIDE_PASSER_BY_DISTANCE[9] = {// 0 1 2 3 4 5 6 7 8 - +0, +0, +7, +14, +21, +28, +34, +41, +55 + +0, +0, +15, +30, +21, +28, +34, +41, +55 }; -static SCORE PASSER_BONUS_AS_MATERIAL_COMES_OFF[32] = -{ +static SCORE PASSER_BONUS_AS_MATERIAL_COMES_OFF[32] = +{ // none na na m na R 2m na - +60, -1, -1, +37, -1, +43, +19, -1, + +92, -1, -1, +53, -1, +3, +19, -1, // Rm 3m/Q 2R R2m 4m/Qm 2Rm R3m/QR Q2m - +14, +9, +20, +4, +3, +3, 0, 0, + +6, +9, +12, +-4, +-5, +-5, 8, 8, // 2R2m R4m/QRm Q3m 2R3m/ QR2m Q4m 2R4m/ QR3m -// Q2R Q2Rm - 0, 0, 0, 0, 0, 0, 0, 0, +// Q2R Q2Rm + 8, 8, 8, 8, 8, 8, 8, 8, // na Q2R2m QR4m na Q2R3m na na full - 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, }; -SCORE RACER_WINS_RACE = +800; +SCORE RACER_WINS_RACE = +784; -static SCORE UNDEVELOPED_MINORS_IN_OPENING[5] = +static SCORE UNDEVELOPED_MINORS_IN_OPENING[5] = {// 0 1 2 3 4 - 0, -6, -10, -16, -22 + -8, -14, -10, -24, -38 }; // // Bishop eval terms // --------------------------------------------------------------------------- // -static SCORE BISHOP_OVER_KNIGHT_IN_ENDGAME = +33; +static SCORE BISHOP_OVER_KNIGHT_IN_ENDGAME = +1; // // "The bishop pair has an average value of half a pawn (more when the @@ -277,12 +277,12 @@ static SCORE BISHOP_OVER_KNIGHT_IN_ENDGAME = +33; // position, and enough to overwhelm most positional considerations. // Moreover, this substantial bishop pair value holds up in all // situations tested, regardless of what else is on the board... -// +// // ...One rule which I often teach to students is that if you have the // bishop pair, and your opponent's single bishop is a bad bishop // (hemmed in by his own pawns), you already have full compensation // for a pawn... Kasparov has said something similar... -// +// // As noted before, the bishop pair is worth more with fewer pawns on // the board. Aside from this factor, the half pawn value of the // bishop pair is remarkably constant, applying even when there are no @@ -291,58 +291,58 @@ static SCORE BISHOP_OVER_KNIGHT_IN_ENDGAME = +33; // "Evaluation of Material Imbalance" static SCORE BISHOP_PAIR[2][17] = { - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 53, 52, 51, 50, 49, 48, 47, 45, 43, 41, 40, 40, 40, 40, 40, 40, 40 } + { -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16 }, + { 21, 68, 51, 34, 41, 48, 55, 37, 59, 57, 40, 40, 40, 40, 40, 40, 40 } }; -static SCORE STATIONARY_PAWN_ON_BISHOP_COLOR[128] = +static SCORE STATIONARY_PAWN_ON_BISHOP_COLOR[128] = { +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0, - -4, -6, -7, -7, -7, -7, -6, -4, 0,0,0,0,0,0,0,0, - -6, -7, -9, -8, -8, -9, -7, -6, 0,0,0,0,0,0,0,0, + -20, -14, -7, -7, -7, -7, -14, -20, 0,0,0,0,0,0,0,0, + -14, -7, -17, -8, -8, -17, -7, -14, 0,0,0,0,0,0,0,0, -7, -8, -10, -11, -11, -10, -8, -7, 0,0,0,0,0,0,0,0, // ------------------------------------------------------------------- -7, -8, -10, -11, -11, -10, -8, -7, 0,0,0,0,0,0,0,0, - -6, -7, -9, -8, -8, -9, -7, -6, 0,0,0,0,0,0,0,0, - -4, -6, -7, -7, -7, -7, -6, -4, 0,0,0,0,0,0,0,0, + -14, -7, -17, -8, -8, -17, -7, -14, 0,0,0,0,0,0,0,0, + -20, -14, -7, -7, -7, -7, -14, -20, 0,0,0,0,0,0,0,0, +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0 }; -static SCORE TRANSIENT_PAWN_ON_BISHOP_COLOR[128] = +static SCORE TRANSIENT_PAWN_ON_BISHOP_COLOR[128] = { +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0, -1, -1, -2, -2, -2, -2, -1, -1, 0,0,0,0,0,0,0,0, - -3, -4, -4, -4, -4, -4, -4, -3, 0,0,0,0,0,0,0,0, - -4, -5, -7, -8, -8, -7, -5, -4, 0,0,0,0,0,0,0,0, + -11, -4, -4, -4, -4, -4, -4, -11, 0,0,0,0,0,0,0,0, + -4, -5, 1, -16, -16, 1, -5, -4, 0,0,0,0,0,0,0,0, // ------------------------------------------------------------------- - -4, -5, -7, -8, -8, -7, -5, -4, 0,0,0,0,0,0,0,0, - -3, -4, -4, -4, -4, -4, -4, -3, 0,0,0,0,0,0,0,0, + -4, -5, 1, -16, -16, 1, -5, -4, 0,0,0,0,0,0,0,0, + -11, -4, -4, -4, -4, -4, -4, -11, 0,0,0,0,0,0,0,0, -1, -1, -2, -2, -2, -2, -1, -1, 0,0,0,0,0,0,0,0, +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0 }; -// +// // A bishop can move to between 0..13 squares. -// +// static SCORE BISHOP_MOBILITY_BY_SQUARES[14] = {// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 - -22, -14, -10, -5, -1, 0, +1, +3, +4, +5, +6, +7, +8, +9 -};// ^ | + -38, -22, -26, -13, -9, 0, +1, +11, +4, +13, +22, +15, +40, +-7 +};// ^ | static SCORE BISHOP_MAX_MOBILITY_IN_A_ROW_BONUS[8] = {// 0 1 2 3 4 5 6 7 - -10, -4, +1, +3, +4, +5, +5, +5 + -18, 4, +9, +3, +12, +-3, +-3, +-3 }; static SCORE BISHOP_UNASSAILABLE_BY_DIST_FROM_EKING[9] = {// 0 1 2 3 4 5 6 7 8 - +0, +33, +28, +18, +8, +4, +0, -10, -16 + +8, +1, +36, +26, +32, +-12, +8, -26, -16 }; static SCORE BISHOP_IN_CLOSED_POSITION[33] = {// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - 10, 10, 10, 9, 9, 8, 8, 7, 7, 6, 5, 4, 3, 2, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 2, 2, 2, 17, 17, 8, 8, 7, 7, -2, 13, 20, 3, 18, 9, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 // 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 }; @@ -350,34 +350,34 @@ static SCORE BISHOP_IN_CLOSED_POSITION[33] = // Knight eval terms // --------------------------------------------------------------------------- // -static SCORE KNIGHT_CENTRALITY_BONUS[128] = +static SCORE KNIGHT_CENTRALITY_BONUS[128] = { - -12, -8, -8, -8, -8, -8, -8, -12, 0,0,0,0,0,0,0,0, - -8, -2, -2, 0, 0, -2, -2, -8, 0,0,0,0,0,0,0,0, - -8, -2, +4, +5, +5, +4, -2, -8, 0,0,0,0,0,0,0,0, - -8, -2, +5, +8, +8, +5, -2, -8, 0,0,0,0,0,0,0,0, + -60, -16, -16, -16, -16, -16, -16, -60, 0,0,0,0,0,0,0,0, + -16, -2, -2, 0, 0, -2, -2, -16, 0,0,0,0,0,0,0,0, + -16, -2, +-4, +13, +13, +-4, -2, -16, 0,0,0,0,0,0,0,0, + -16, -2, +13, +16, +16, +13, -2, -16, 0,0,0,0,0,0,0,0, // ------------------------------------------------------------------- - -8, -2, +5, +8, +8, +5, -2, -8, 0,0,0,0,0,0,0,0, - -8, -2, +4, +5, +5, +4, -2, -8, 0,0,0,0,0,0,0,0, - -8, -2, -2, 0, 0, -2, -2, -8, 0,0,0,0,0,0,0,0, - -12, -8, -8, -8, -8, -8, -8, -12, 0,0,0,0,0,0,0,0 + -16, -2, +13, +16, +16, +13, -2, -16, 0,0,0,0,0,0,0,0, + -16, -2, +-4, +13, +13, +-4, -2, -16, 0,0,0,0,0,0,0,0, + -16, -2, -2, 0, 0, -2, -2, -16, 0,0,0,0,0,0,0,0, + -60, -16, -16, -16, -16, -16, -16, -60, 0,0,0,0,0,0,0,0 }; static SCORE KNIGHT_KING_TROPISM_BONUS[9] = {// 0 1 2 3 4 5 6 7 8 - 0, +15, +12, +9, +4, +0, +0, +0, +0 + 8, +-9, +12, +1, +12, +8, +8, +8, +8 }; static SCORE KNIGHT_UNASSAILABLE_BY_DIST_FROM_EKING[9] = {// 0 1 2 3 4 5 6 7 8 - +0, +25, +19, +14, +6, +0, +0, +0, +0 + +-24, +9, +11, +38, +14, +-24, +-24, +-24, +-24 }; static SCORE KNIGHT_ON_INTERESTING_SQUARE_BY_RANK[2][9] = {// 0 1 2 3 4 5 6 7 8 - { +0, +12, +11, +9, +6, +3, +0, +0, +0 }, // black - { +0, +0, +0, +0, +3, +6, +9, +11, +12 } // white + { +-8, +4, +27, +25, +22, +3, +-8, +-8, +-8 }, // black + { +-8, +-8, +-8, +-8, +3, +22, +25, +27, +4 } // white }; // @@ -387,23 +387,23 @@ static SCORE KNIGHT_ON_INTERESTING_SQUARE_BY_RANK[2][9] = // 1 = unsupported enemy pawn, friend pawn, enemy B/N // enemy controlled empty sq. // 2 = enemy >B, friend controlled empty sq, no one controlled empty sq -// +// // Total: 16 mobility max -// +// static SCORE KNIGHT_MOBILITY_BY_COUNT[9] = {// 0 1 2 3 4 5 6 7 8 - -17, -10, -6, 0, +3, +5, +7, +8, +8 + -33, -18, -14, -8, +-5, +5, +15, +8, +8 };// ^ | -static SCORE KNIGHT_WITH_N_PAWNS_SUPPORTING[3] = +static SCORE KNIGHT_WITH_N_PAWNS_SUPPORTING[3] = { - +0, +4, +8 + +16, +12, +0 }; static SCORE KNIGHT_IN_CLOSED_POSITION[33] = {// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 9, 11, - 12, 13, 15, 17, 19, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 27 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 18, 4, 6, 17, -13, + 12, 5, -17, 1, 19, -3, -3, 30, 30, 23, 23, 24, 24, 25, 25, 26, 27 // 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 }; @@ -413,44 +413,44 @@ static SCORE KNIGHT_IN_CLOSED_POSITION[33] = // static SCORE ROOK_ON_FULL_OPEN_BY_DIST_FROM_EKING[8] = {// 0 1 2 3 4 5 6 7 - +24, +22, +17, +14, +13, +13, +12, +12 + +48, +38, +41, +38, +29, +29, +20, +20 }; -static SCORE ROOK_ON_HALF_OPEN_WITH_ENEMY_BY_DIST_FROM_EKING[8] = +static SCORE ROOK_ON_HALF_OPEN_WITH_ENEMY_BY_DIST_FROM_EKING[8] = {// 0 1 2 3 4 5 6 7 - +12, +11, +9, +9, +8, +8, +8, +7 + +12, +11, +17, +17, +32, +32, +32, +15 }; static SCORE ROOK_ON_HALF_OPEN_WITH_FRIEND_BY_DIST_FROM_EKING[8] = {// 0 1 2 3 4 5 6 7 - +13, +12, +11, +11, +9, +9, +9, +8 + +-3, +12, +11, +11, +17, +17, +17, +16 }; static SCORE ROOK_BEHIND_PASSER_BY_PASSER_RANK[2][9] = {// 0 1 2 3 4 5 6 7 8 - { +0, +0, +25, +17, +12, +6, +1, +0, +0 }, // black - { +0, +0, +0, +1, +6, +12, +17, +25, +0 } // white + { +-24, +-24, +41, +17, +4, +-2, +-31, +-24, +-24 }, // black + { +-24, +-24, +-24, +-31, +-2, +4, +17, +41, +-24 } // white }; static SCORE ROOK_LEADS_PASSER_BY_PASSER_RANK[2][9] = {// 0 1 2 3 4 5 6 7 8 - { +0, +0, -22, -16, -13, -9, -5, -3, +0 }, // black - { +0, +0, -3, -5, -9, -13, -16, -22, +0 } // white + { +0, +0, -54, -16, 11, -9, 19, -3, +0 }, // black + { +0, +0, -3, 19, -9, 11, -16, -54, +0 } // white }; -static SCORE KING_TRAPPING_ROOK = -40; +static SCORE KING_TRAPPING_ROOK = -48; -static SCORE ROOK_TRAPPING_EKING = +22; +static SCORE ROOK_TRAPPING_EKING = +46; -static SCORE ROOK_VALUE_AS_PAWNS_COME_OFF[17] = -{// 0 1 2 3 4 5 6 7 8 - +55, +51, +44, +38, +33, +27, +22, +16, +7, +static SCORE ROOK_VALUE_AS_PAWNS_COME_OFF[17] = +{// 0 1 2 3 4 5 6 7 8 + +7, +35, +92, +46, +33, +-5, +-10, +16, +-17, // 9 10 11 12 13 14 15 16 - +1, -3, -6, -9, -12, -18, -22, -25 + +-15, -27, -38, -57, -44, -42, -70, -41 }; // -// Note: these are multiplied by two (one per rook). +// Note: these are multiplied by two (one per rook). // static SCORE ROOK_CONNECTED_VERT = +7; // x2 static SCORE ROOK_CONNECTED_HORIZ = +4; // x2 @@ -461,17 +461,17 @@ static SCORE ROOK_CONNECTED_HORIZ = +4; // x2 // 0 = supported enemy P/N/B, friend piece // 1 = unsupported enemy piece, enemy controlled empty sq, enemy R // 2 = enemy >R, friend controlled empty sq, no one controlled empty sq -// +// // Total: 28 mobility max -// +// static SCORE ROOK_MOBILITY_BY_SQUARES[15] = {// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - -28, -24, -20, -14, -7, -2, +0, +4, +8, +12, +15, +17, +19, +21, +22 + -36, -16, -20, -14, -7, -10, +0, +4, +8, +12, +15, +9, +19, +13, +14 };// ^ | static SCORE ROOK_MAX_MOBILITY_IN_A_ROW_BONUS[8] = {// 0 1 2 3 4 5 6 7 - -15, -6, +0, +4, +8, +8, +8, +8 + 1, 10, +8, +12, +8, +8, +8, +8 }; // @@ -486,21 +486,21 @@ static SCORE ROOK_MAX_MOBILITY_IN_A_ROW_BONUS[8] = // 0 = supported enemy P/N/B/R, friend piece // 1 = unsupported enemy piece, enemy controlled empty sq, enemy Q // 2 = enemy K, no one controlled empty sq, friend controlled empty sq -// +// // Total: 54 mobility max -// +// static SCORE QUEEN_MOBILITY_BY_SQUARES[28] = {// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - -30, -26, -22, -17, -11, -8, -4, -2, -1, +2, +4, +7, +10, +12, +14, + -54, -34, -22, -17, -19, -8, -4, -2, 7, +10, +12, +15, +18, +4, +30, // | //15 16 17 18 19 20 21 22 23 24 25 26 27 - +15, +16, +17, +18, +19, +20, +20, +21, +21, +22, +22, +23, +23 + +31, +32, +41, +42, +35, +36, +36, +21, +21, +6, +6, +7, +7 }; -static SCORE QUEEN_OUT_EARLY[5] = +static SCORE QUEEN_OUT_EARLY[5] = {// 0 1 2 3 4 : num unmoved minors - 0, -14, -22, -26, -33 + 0, -14, -6, -2, -25 }; // @@ -508,18 +508,18 @@ static SCORE QUEEN_OUT_EARLY[5] = // point that queens are worth more then the standard nine "points". // Thus, this scale is biased upwards by about 0.15 pawn. // -static SCORE QUEEN_KING_TROPISM[8] = +static SCORE QUEEN_KING_TROPISM[8] = {// 0 1 2 3 4 5 6 7 - 0, +34, +28, +22, +19, +17, +16, +15 + 0, +34, +76, +30, +27, +9, +8, +-1 }; -static SCORE QUEEN_ATTACKS_SQ_NEXT_TO_KING = 8; // x #sq_attacked +static SCORE QUEEN_ATTACKS_SQ_NEXT_TO_KING = -8; // x #sq_attacked // // King eval terms // --------------------------------------------------------------------------- // -static ULONG KING_INITIAL_COUNTER_BY_LOCATION[2][128] = +static ULONG KING_INITIAL_COUNTER_BY_LOCATION[2][128] = { { 1, 0, 0, 0, 1, 0, 0, 1, 0,0,0,0,0,0,0,0, @@ -545,17 +545,17 @@ static ULONG KING_INITIAL_COUNTER_BY_LOCATION[2][128] = } }; -static SCORE KING_TO_CENTER[128] = +static SCORE KING_TO_CENTER[128] = { - +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0, - +1, +3, +5, +7, +7, +5, +3, +1, 0,0,0,0,0,0,0,0, - +3, +5, +13, +15, +15, +13, +5, +3, 0,0,0,0,0,0,0,0, - +5, +7, +17, +23, +23, +17, +7, +5, 0,0,0,0,0,0,0,0, + +-32, +-32, +-32, +-32, +-32, +-32, +-32, +-32, -32,-32,-32,-32,-32,-32,-32,-32, + +-23, +3, +5, +-1, +-1, +5, +3, +-23, -32,-32,-32,-32,-32,-32,-32,-32, + +3, +5, +13, +15, +15, +13, +5, +3, -32,-32,-32,-32,-32,-32,-32,-32, + +5, +-1, +41, +31, +31, +41, +-1, +5, -32,-32,-32,-32,-32,-32,-32,-32, // ------------------------------------------------------------------ - +5, +7, +17, +23, +23, +17, +7, +5, 0,0,0,0,0,0,0,0, - +3, +5, +13, +15, +15, +13, +5, +3, 0,0,0,0,0,0,0,0, - +1, +3, +5, +7, +7, +5, +3, +1, 0,0,0,0,0,0,0,0, - +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0 + +5, +-1, +41, +31, +31, +41, +-1, +5, -32,-32,-32,-32,-32,-32,-32,-32, + +3, +5, +13, +15, +15, +13, +5, +3, -32,-32,-32,-32,-32,-32,-32,-32, + +-23, +3, +5, +-1, +-1, +5, +3, +-23, -32,-32,-32,-32,-32,-32,-32,-32, + +-32, +-32, +-32, +-32, +-32, +-32, +-32, +-32, -32,-32,-32,-32,-32,-32,-32,-32 }; // @@ -573,7 +573,7 @@ static SCORE KING_TO_CENTER[128] = #define KEEP_KING_AT_HOME_THRESHOLD \ (VALUE_ROOK + VALUE_ROOK + VALUE_BISHOP + VALUE_KING + 1) -static ULONG KING_COUNTER_BY_ATTACK_PATTERN[32] = +static ULONG KING_COUNTER_BY_ATTACK_PATTERN[32] = { //p 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 //m 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 @@ -585,13 +585,13 @@ static ULONG KING_COUNTER_BY_ATTACK_PATTERN[32] = static SCORE KING_SAFETY_BY_COUNTER[42] = { - -0, -4, -8, -12, -16, -20, -25, -31, -38, -50, -63, -76, -90, - -105,-120,-135,-150,-165,-180,-195,-210,-225,-240,-255,-270,-290, - -310,-330,-350,-370,-390,-410,-440,-470,-500,-500,-500,-500,-500, - -500,-500,-500 + 0, -4, -40, -28, -24, -28, -33, -39, -30, -50, -55, -60, -90, + -97,-104,-135,-150,-141,-148,-211,-210,-217,-240,-255,-286,-306, + -262,-330,-350,-386,-390,-378,-448,-462,-508,-508,-508,-508,-508, + -508,-508,-508 }; -static SCORE KING_MISSING_ONE_CASTLE_OPTION = -23; +static SCORE KING_MISSING_ONE_CASTLE_OPTION = 9; typedef struct _DNA_BASE_SIZE { SCORE *pBase; ULONG uCount; @@ -659,8 +659,8 @@ static DNA_BASE_SIZE g_EvalDNA[] = { DNA_VAR(KING_MISSING_ONE_CASTLE_OPTION) }; -ULONG -DNABufferSizeBytes() +ULONG +DNABufferSizeBytes() { ULONG u; ULONG uSize = 0; @@ -672,14 +672,14 @@ DNABufferSizeBytes() char * -ExportEvalDNA() +ExportEvalDNA() { ULONG uSize = DNABufferSizeBytes(); char *p = malloc(uSize); ULONG u, v; SCORE *q; FLAG fFirst = TRUE; - + memset(p, 0, uSize); for (u = 0; u < ARRAY_LENGTH(g_EvalDNA); u++) { q = g_EvalDNA[u].pBase; @@ -735,7 +735,7 @@ ImportEvalDNA(char *p) } FLAG -ReadEvalDNA(char *szFilename) +ReadEvalDNA(char *szFilename) { FILE *p = NULL; ULONG uSize = DNABufferSizeBytes(); @@ -795,31 +795,31 @@ BITBOARD BBADJACENT_FILES[8] = { // A = 0 BBFILEB, - + // B = 1 BBFILEA | BBFILEC, - + // C = 2 BBFILEB | BBFILED, - + // D = 3 BBFILEC | BBFILEE, - + // E = 4 BBFILED | BBFILEF, - + // F = 5 BBFILEE | BBFILEG, - + // G = 6 BBFILEF | BBFILEH, - + // H = 7 BBFILEG }; // -// The ranks preceeding one, indexed by rank/color of pawn (used in +// The ranks preceeding one, indexed by rank/color of pawn (used in // eval.c) // BITBOARD BBPRECEEDING_RANKS[8][2] = @@ -853,15 +853,15 @@ BITBOARD BBPRECEEDING_RANKS[8][2] = }; -static FLAG -_IsSquareSafeFromEnemyPawn(IN POSITION *pos, - IN COOR c, +static FLAG +_IsSquareSafeFromEnemyPawn(IN POSITION *pos, + IN COOR c, IN BITBOARD bb) /** Routine description: - Determine if a piece in square c is "outposted". A piece is + Determine if a piece in square c is "outposted". A piece is outposted if no enemy pawns can advance to attack it / drive it away. @@ -888,16 +888,16 @@ Return value: ASSERT(p && (IS_BISHOP(p) || IS_KNIGHT(p) || IS_PAWN(p))); ASSERT(IS_VALID_COLOR(uColor)); #endif - + uFile = FILE(c); ASSERT(uFile < 8); bb &= BBADJACENT_FILES[uFile]; - + ASSERT(((c & 0x70) >> 4) == (c >> 4)); uRank = c >> 4; ASSERT(uRank < 8); bb &= BBPRECEEDING_RANKS[uRank][uColor]; - + #ifdef DEBUG if (bb != 0) { @@ -960,8 +960,8 @@ Return value: } -static ULONG -_WhoControlsSquareFast(IN POSITION *pos, +static ULONG +_WhoControlsSquareFast(IN POSITION *pos, IN COOR c) /** @@ -979,7 +979,7 @@ Return value: static ULONG : (ULONG)-1 if neither side controls it or the sides control it evenly, WHITE if white controls the square, or BLACK if black controls the square. - + TODO: fix this to use counts / xrays **/ @@ -991,7 +991,7 @@ Return value: ULONG u; PIECE p; CHAR ch; - + ASSERT((c + 8) == (c | 8)); ASSERT((uWhite & 0xFFFFFF00) == 0); ASSERT((uBlack & 0xFFFFFF00) == 0); @@ -999,7 +999,7 @@ Return value: // TODO: keep these and update the table to use them uWhite >>= 3; uBlack >>= 3; - + p = pos->rgSquare[c].pPiece; // p -= 2; ch = g_SwapTable[p][uWhite][uBlack]; @@ -1053,7 +1053,7 @@ Return value: CLEAR_A_SQ; c++; \ CLEAR_A_SQ; c += 10; -static void +static void _ClearAttackTables(IN OUT POSITION *pos) { register COOR c = 8; @@ -1087,8 +1087,8 @@ _ClearAttackTables(IN OUT POSITION *pos) } -static INLINE void -_InitializePawnHashEntry(IN OUT PAWN_HASH_ENTRY *pHash, +static INLINE void +_InitializePawnHashEntry(IN OUT PAWN_HASH_ENTRY *pHash, IN POSITION *pos) /** @@ -1149,11 +1149,11 @@ Return value: ASSERT(IS_PAWN(pHelper)); uColor = GET_COLOR(pHelper); ASSERT(IS_VALID_COLOR(uColor)); - + if (pHash->uCountPerFile[FLIP(uColor)][uPawnFile] != 0) { ASSERT((pHash->bbPawnLocations[FLIP(uColor)] & BBFILE[FILE(c)]) != 0); - + // // The only way a pawn can be a passer/candidate if the other // side has a pawn on its same file is if that enemy pawn is @@ -1182,12 +1182,12 @@ Return value: break; } } - + // // Count FLIP(uColor)'s sentries and determine the location of the - // critical square. Note if there are no sentries then this + // critical square. Note if there are no sentries then this // pawn (on square c) is a passer already, not a candidate. - // + // bb = pHash->bbPawnLocations[FLIP(uColor)] & BBADJACENT_FILES[FILE(c)]; bb &= BBPRECEEDING_RANKS[(c & 0x70) >> 4][uColor]; if (!bb) @@ -1206,11 +1206,11 @@ Return value: pHash->iScore[uColor], PASSER_BY_RANK[uColor][RANK(c)], "passed pawn"); - + // // However, don't give doubled passers such a big bonus. // - if (IS_PAWN(pos->rgSquare[c - 16 * g_iAhead[uColor]].pPiece)) + if (IS_PAWN(pos->rgSquare[c - 16 * g_iAhead[uColor]].pPiece)) { EVAL_TERM(uColor, PAWN, @@ -1256,7 +1256,7 @@ Return value: if (pos->rgSquare[c1 - 1].pPiece == pSentry) { uVerifySentries++; - if (0 == cVerifySquare) + if (0 == cVerifySquare) { cVerifySquare = c1 - d1; ASSERT(cVerifySquare == cSquare); @@ -1308,7 +1308,7 @@ Return value: uHelpers = 1; goto do_left; } - + // // There is no helper pawn in the support position yet. // See if one can get there. @@ -1331,7 +1331,7 @@ Return value: } } } - + do_left: c1 = cSquare - 1 - d1; if ((IS_ON_BOARD(c1)) && (pHash->uCountPerFile[uColor][FILE(c1) + 1])) @@ -1350,7 +1350,7 @@ Return value: uHelpers++; goto done_helpers; } - + // // There is no pawn in the left support position yet. See // if one can get there. @@ -1373,7 +1373,7 @@ Return value: } } } - + done_helpers: if ((uHelpers >= uSentries) || ((pHash->uCountPerFile[uColor][uPawnFile + 1] + @@ -1405,7 +1405,7 @@ Return value: } } -static void +static void _EvaluateConnectedSupportedOutsidePassers(IN POSITION *pos, IN OUT PAWN_HASH_ENTRY *pHash) /** @@ -1430,15 +1430,15 @@ Return value: PIECE pFriend; COOR cSupport; BITBOARD bb; - static const INT iDelta[5] = + static const INT iDelta[5] = { -1, +1, +17, +15, 0 }; - + FOREACH_COLOR(uColor) { ASSERT(IS_VALID_COLOR(uColor)); - + if (pHash->bbPasserLocations[uColor]) { ASSERT(pos->uPawnCount[uColor] > 0); @@ -1464,14 +1464,14 @@ Return value: cLeftmostPasser = c; } cRightmostPasser = c; - + v = 0; while(iDelta[v] != 0) { cSupport = c + iDelta[v] * g_iBehind[uColor]; if (IS_ON_BOARD(cSupport)) { - if (pHash->bbPasserLocations[uColor] & + if (pHash->bbPasserLocations[uColor] & COOR_TO_BB(cSupport)) { ASSERT(CONNECTED_PASSERS_BY_RANK[uColor] @@ -1489,7 +1489,7 @@ Return value: // strong. // } - else if (pos->rgSquare[cSupport].pPiece == + else if (pos->rgSquare[cSupport].pPiece == pFriend) { ASSERT(SUPPORTED_PASSER_BY_RANK[uColor] @@ -1508,7 +1508,7 @@ Return value: } } } - + #ifdef DEBUG ASSERT(IS_ON_BOARD(cLeftmostPasser)); ASSERT(IS_ON_BOARD(cRightmostPasser)); @@ -1565,12 +1565,12 @@ Return value: } } } - + #define PAWN_ATTACK_BLACK_DELTA (+15) #define PAWN_ATTACK_WHITE_DELTA (-17) -static void +static void _PopulatePawnAttackBits(IN OUT POSITION *pos) /** @@ -1607,7 +1607,7 @@ Return value: ASSERT(pos->uPawnCount[BLACK] <= 8); for (u = 0; - u < pos->uPawnCount[BLACK]; + u < pos->uPawnCount[BLACK]; u++) { c = pos->cPawns[BLACK][u]; @@ -1615,10 +1615,10 @@ Return value: ASSERT(pos->rgSquare[c].pPiece); ASSERT(IS_PAWN(pos->rgSquare[c].pPiece)); ASSERT(GET_COLOR(pos->rgSquare[c].pPiece) == BLACK); - + // // IDEA: These IS_ON_BOARD checks can be removed by a lookup table. - // + // cAttack = c + PAWN_ATTACK_BLACK_DELTA; if (IS_ON_BOARD(cAttack)) { @@ -1638,7 +1638,7 @@ Return value: ASSERT(pos->uPawnCount[WHITE] <= 8); for (u = 0; - u < pos->uPawnCount[WHITE]; + u < pos->uPawnCount[WHITE]; u++) { c = pos->cPawns[WHITE][u]; @@ -1646,7 +1646,7 @@ Return value: ASSERT(pos->rgSquare[c].pPiece); ASSERT(IS_PAWN(pos->rgSquare[c].pPiece)); ASSERT(GET_COLOR(pos->rgSquare[c].pPiece) == WHITE); - + cAttack = c + PAWN_ATTACK_WHITE_DELTA; if (IS_ON_BOARD(cAttack)) { @@ -1664,10 +1664,10 @@ Return value: } } } - + static PAWN_HASH_ENTRY * -_EvalPawns(IN OUT SEARCHER_THREAD_CONTEXT *ctx, +_EvalPawns(IN OUT SEARCHER_THREAD_CONTEXT *ctx, OUT FLAG *pfDeferred) /** @@ -1698,7 +1698,7 @@ Return value: ULONG uUnsupportable; BITBOARD bb; int d1; -#ifdef DEBUG +#ifdef DEBUG SCORE t; #endif @@ -1734,10 +1734,10 @@ Return value: { ASSERT(IS_VALID_COLOR(uColor)); ASSERT(pos->uPawnCount[uColor] <= 8); - + d1 = 16 * g_iAhead[uColor]; - for (u = 0; - u < pos->uPawnCount[uColor]; + for (u = 0; + u < pos->uPawnCount[uColor]; u++) { c = pos->cPawns[uColor][u]; @@ -1746,17 +1746,17 @@ Return value: ASSERT(IS_PAWN(pos->rgSquare[c].pPiece)); uPawnFile = FILE(c) + 1; ASSERT((1 <= uPawnFile) && (uPawnFile <= 8)); - + // // Update files counter // ASSERT(pHash->uCountPerFile[uColor][uPawnFile] < 6); pHash->uCountPerFile[uColor][uPawnFile]++; - + // // Update bitboard bit // - ASSERT(CountBits(pHash->bbPawnLocations[uColor] & + ASSERT(CountBits(pHash->bbPawnLocations[uColor] & BBFILE[uPawnFile-1]) < 6); ASSERT((pHash->bbPawnLocations[uColor] & COOR_TO_BB(c)) == 0); pHash->bbPawnLocations[uColor] |= COOR_TO_BB(c); @@ -1767,16 +1767,16 @@ Return value: // // Count unmoved pawns // - pHash->uNumUnmovedPawns[uColor] += + pHash->uNumUnmovedPawns[uColor] += ((c & 0xF0) == uUnmovedRank[uColor]); ASSERT(pHash->uNumUnmovedPawns[uColor] <= 8); - + // // Detect rammed and other stationary pawns. // cSquare = c + d1; ASSERT(IS_ON_BOARD(cSquare)); - p = pos->rgSquare[cSquare].pPiece; + p = pos->rgSquare[cSquare].pPiece; if (IS_PAWN(p)) { pHash->bbStationaryPawns[uColor] |= COOR_TO_BB(c); @@ -1806,15 +1806,15 @@ Return value: // // Second pass - // + // FOREACH_COLOR(uColor) { ASSERT(IS_VALID_COLOR(uColor)); ASSERT(pos->uPawnCount[uColor] <= 8); ASSERT(CountBits(pHash->bbPawnLocations[uColor]) <= 8); - + d1 = 16 * g_iAhead[uColor]; - for (u = 0; + for (u = 0; u < pos->uPawnCount[uColor]; u++) { @@ -1822,7 +1822,7 @@ Return value: ASSERT(IS_ON_BOARD(c)); ASSERT(IS_PAWN(pos->rgSquare[c].pPiece)); ASSERT((1 < RANK(c)) && (RANK(c) < 8)); - + // // Find weak pawns... pawns that are isolated are weak as // are pawns that have advanced beyond their support. @@ -1833,14 +1833,14 @@ Return value: uPawnFile = FILE(c) - 1; if (pHash->uCountPerFile[uColor][uPawnFile + 1] > 0) { - bb = (pHash->bbPawnLocations[uColor] & + bb = (pHash->bbPawnLocations[uColor] & BBFILE[uPawnFile] & BBADJACENT_RANKS[RANK(c)]); if (!bb) { bb = (pHash->bbPawnLocations[FLIP(uColor)] & BBFILE[uPawnFile]); - while(IS_ON_BOARD(cSquare = + while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb))) { ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece)); @@ -1879,14 +1879,14 @@ Return value: uPawnFile = FILE(c) + 1; if (pHash->uCountPerFile[uColor][uPawnFile + 1] > 0) { - bb = (pHash->bbPawnLocations[uColor] & + bb = (pHash->bbPawnLocations[uColor] & BBFILE[uPawnFile] & BBADJACENT_RANKS[RANK(c)]); if (!bb) { bb = (pHash->bbPawnLocations[FLIP(uColor)] & BBFILE[uPawnFile]); - while(IS_ON_BOARD(cSquare = + while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb))) { ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece)); @@ -1916,7 +1916,7 @@ Return value: { uUnsupportable = 2; } - + // // If this pawn is not supportable from either side then // it is either isolated or has been pushed too far ahead @@ -1941,17 +1941,17 @@ Return value: PAWN, c, pHash->iScore[uColor], - ((pHash->uCountPerFile[FLIP(uColor)][uPawnFile]==0)* + ((pHash->uCountPerFile[FLIP(uColor)][uPawnFile]==0)* ISOLATED_EXPOSED_PAWN), "exposed target"); - + // // Exponential penalty term // uIsolated[uColor] += 1; ASSERT(uIsolated[uColor] > 0); ASSERT(uIsolated[uColor] <= 8); - + // // Isolated + doubled? Extra penalty. // @@ -2010,7 +2010,7 @@ Return value: { // // Detect backwards pawns. See "Pawn Power in Chess" pp 25-27 - // + // if (!IS_PAWN(pos->rgSquare[cSquare].pPiece)) { p = (BLACK_PAWN | uColor); @@ -2055,19 +2055,19 @@ Return value: // // TODO: Think about backward doubled pawns; read Kauffman's // paper "All About Doubled Pawns". - // + // } // // Handle passers / candidate passers - // + // _EvaluateCandidatePasser(pos, pHash, c); } } - + // // Reward pawn duos, see "Pawn Power in Chess" pp. 10-16 - // + // ASSERT(iDuos[WHITE] >= 0); ASSERT(iDuos[WHITE] <= 56); ASSERT(iDuos[BLACK] >= 0); @@ -2128,9 +2128,9 @@ Return value: pHash->iScore[WHITE], ISOLATED_PAWN_PENALTY_BY_COUNT[uIsolated[WHITE]], "exponential isolated"); - + // - // Look for connected, supported and outside passed pawns to give + // Look for connected, supported and outside passed pawns to give // extra bonuses. // _EvaluateConnectedSupportedOutsidePassers(pos, pHash); @@ -2142,7 +2142,7 @@ Return value: } -ULONG +ULONG CountKingSafetyDefects(IN OUT POSITION *pos, IN ULONG uSide) /** @@ -2170,11 +2170,11 @@ Return value: int i; PIECE p; ULONG u; - + // // Don't count king safety defects if the real eval code in // _EvalKing would not... - // + // if (pos->uNonPawnMaterial[xSide] < DO_KING_SAFETY_THRESHOLD) { return 0; } @@ -2185,7 +2185,7 @@ Return value: ASSERT(pos->rgSquare[cKing].uIndex == 0); ASSERT(IS_ON_BOARD(cKing)); ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == uSide); - + // // Make sure cKing - 1, cKing and cKing + 1 are on the board // @@ -2218,16 +2218,16 @@ Return value: ((CHECK_VECTOR_WITH_INDEX(i + 2, xSide) & p) != 0)); } ASSERT(uCounter < 15); - + // Save the number of enemy pieces pointing at this king for later use. - pos->uPiecesPointingAtKing[uSide] = + pos->uPiecesPointingAtKing[uSide] = (uCounter - (KING_INITIAL_COUNTER_BY_LOCATION[uSide][cKing] >> 1)); return uCounter; } static void -_QuicklyEstimateKingSafetyTerm(IN POSITION *pos, +_QuicklyEstimateKingSafetyTerm(IN POSITION *pos, IN OUT SCORE *piAlphaMargin, IN OUT SCORE *piBetaMargin) /** @@ -2258,12 +2258,12 @@ Return value: SCORE iRet; ASSERT(CountKingSafetyDefects(pos, pos->uToMove) < 15); - iPenaltyEst[WHITE] = + iPenaltyEst[WHITE] = iScoreByDefectCount[CountKingSafetyDefects(pos, WHITE)]; ASSERT(iPenaltyEst[WHITE] > 0); ASSERT(CountKingSafetyDefects(pos, BLACK) < 15); - iPenaltyEst[BLACK] = + iPenaltyEst[BLACK] = iScoreByDefectCount[CountKingSafetyDefects(pos, BLACK)]; ASSERT(iPenaltyEst[BLACK] > 0); @@ -2293,7 +2293,7 @@ static void _QuicklyEstimatePasserBonuses(POSITION *pos, PAWN_HASH_ENTRY *pHash, SCORE *piAlphaMargin, - SCORE *piBetaMargin) + SCORE *piBetaMargin) { SCORE iBonusEst[2] = {0, 0}; SCORE iRet; @@ -2308,7 +2308,7 @@ _QuicklyEstimatePasserBonuses(POSITION *pos, u /= VALUE_PAWN; u = MINU(31, u); ASSERT(PASSER_BONUS_AS_MATERIAL_COMES_OFF[u] >= 0); - iBonusEst[WHITE] = + iBonusEst[WHITE] = (CountBits(pHash->bbPasserLocations[WHITE]) * PASSER_BONUS_AS_MATERIAL_COMES_OFF[u]); } @@ -2318,7 +2318,7 @@ _QuicklyEstimatePasserBonuses(POSITION *pos, u /= VALUE_PAWN; u = MINU(31, u); ASSERT(PASSER_BONUS_AS_MATERIAL_COMES_OFF[u] >= 0); - iBonusEst[BLACK] = + iBonusEst[BLACK] = (CountBits(pHash->bbPasserLocations[BLACK]) * PASSER_BONUS_AS_MATERIAL_COMES_OFF[u]); } @@ -2334,7 +2334,7 @@ _QuicklyEstimatePasserBonuses(POSITION *pos, // // For the alpha bonus, assume our passer bonus gets doubled and - // our opponent's is reduced. Net bonus to us / their bonus not + // our opponent's is reduced. Net bonus to us / their bonus not // as high. // iRet = iBonusEst[pos->uToMove] - iBonusEst[FLIP(pos->uToMove)] / 2; @@ -2343,9 +2343,9 @@ _QuicklyEstimatePasserBonuses(POSITION *pos, } -static void -_InvalidEvaluator(UNUSED POSITION *pos, - UNUSED COOR c, +static void +_InvalidEvaluator(UNUSED POSITION *pos, + UNUSED COOR c, UNUSED PAWN_HASH_ENTRY *pHash) /** @@ -2374,7 +2374,7 @@ Return value: // ====================================================================== // -static FLAG FASTCALL +static FLAG FASTCALL _InvalidMobilityHelper(UNUSED POSITION *pos, UNUSED COOR c, UNUSED ULONG *puMobility, @@ -2406,7 +2406,7 @@ Return value: // ---------------------------------------------------------------------- -static FLAG FASTCALL +static FLAG FASTCALL _BMinorToEnemyLess(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -2433,7 +2433,7 @@ Return value: #ifdef DEBUG PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; PIECE p = pos->rgSquare[c].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(!IS_EMPTY(p)); @@ -2449,7 +2449,7 @@ Return value: return(TRUE); // stop scanning this dir } -static FLAG FASTCALL +static FLAG FASTCALL _WMinorToEnemyLess(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -2474,7 +2474,7 @@ Return value: #ifdef DEBUG PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; PIECE p = pos->rgSquare[c].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(!IS_EMPTY(p)); @@ -2490,7 +2490,7 @@ Return value: return(TRUE); // stop scanning this dir } -static FLAG FASTCALL +static FLAG FASTCALL _WRookToEnemyLess(POSITION *pos, COOR c, ULONG *puMobility, @@ -2515,7 +2515,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; - + ASSERT(IS_ROOK(pRook)); ASSERT(!IS_EMPTY(p)); ASSERT(OPPOSITE_COLORS(p, pRook)); @@ -2527,7 +2527,7 @@ Return value: return(TRUE); // stop scanning this dir } -static FLAG FASTCALL +static FLAG FASTCALL _BRookToEnemyLess(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -2552,7 +2552,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(IS_ROOK(pRook)); @@ -2566,7 +2566,7 @@ Return value: } -static FLAG FASTCALL +static FLAG FASTCALL _BQueenToEnemyLess(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -2591,7 +2591,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(IS_QUEEN(pQueen)); @@ -2605,7 +2605,7 @@ Return value: } -static FLAG FASTCALL +static FLAG FASTCALL _WQueenToEnemyLess(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -2630,7 +2630,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(IS_QUEEN(pQueen)); @@ -2645,7 +2645,7 @@ Return value: // ---------------------------------------------------------------------- -static FLAG FASTCALL +static FLAG FASTCALL _EMinorToEnemySame(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -2670,13 +2670,13 @@ Return value: #ifdef DEBUG PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; PIECE p = pos->rgSquare[c].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(!IS_EMPTY(p)); ASSERT(!IS_EMPTY(pMinor)); ASSERT(OPPOSITE_COLORS(p, pMinor)); - + if (IS_BISHOP(pMinor)) { ASSERT(PIECE_VALUE(p) == VALUE_BISHOP); @@ -2693,7 +2693,7 @@ Return value: } -static FLAG FASTCALL +static FLAG FASTCALL _ERookToEnemySame(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -2718,7 +2718,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(IS_ROOK(pRook)); @@ -2732,7 +2732,7 @@ Return value: } -static FLAG FASTCALL +static FLAG FASTCALL _EQueenToEnemyGreaterEqual(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -2757,7 +2757,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(IS_QUEEN(pQueen)); @@ -2772,7 +2772,7 @@ Return value: // ---------------------------------------------------------------------- -static FLAG FASTCALL +static FLAG FASTCALL _EMinorToEnemyGreater(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -2797,7 +2797,7 @@ Return value: #ifdef DEBUG PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; PIECE p = pos->rgSquare[c].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(!IS_EMPTY(p)); @@ -2811,7 +2811,7 @@ Return value: return(FALSE); // keep scanning this dir } -static FLAG FASTCALL +static FLAG FASTCALL _ERookToEnemyGreater(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -2852,7 +2852,7 @@ Return value: // ---------------------------------------------------------------------- -static FLAG FASTCALL +static FLAG FASTCALL _AnythingToFriendNoXray(IN POSITION *pos, IN COOR c, UNUSED ULONG *puMobility, @@ -2878,7 +2878,7 @@ Return value: COOR cMover = pos->cPiece; PIECE pMover = pos->rgSquare[cMover].pPiece; PIECE pFriend = pos->rgSquare[c].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(cMover)); ASSERT(!IS_EMPTY(pFriend)); @@ -2906,7 +2906,7 @@ Return value: // ---------------------------------------------------------------------- -static FLAG FASTCALL +static FLAG FASTCALL _EMinorToFriendXray(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -2931,7 +2931,7 @@ Return value: #ifdef DEBUG PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; PIECE p = pos->rgSquare[c].pPiece; - + ASSERT(!IS_EMPTY(p)); ASSERT(!IS_EMPTY(pMinor)); ASSERT(IS_BISHOP(pMinor)); @@ -2941,7 +2941,7 @@ Return value: return(FALSE); } -static FLAG FASTCALL +static FLAG FASTCALL _BRookToFriendRook(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -2968,7 +2968,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pRook = pos->rgSquare[cRook].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(cRook)); ASSERT(IS_ROOK(pRook)); @@ -2983,7 +2983,7 @@ Return value: ROOK, c, pos->iScore[BLACK], - (ROOK_CONNECTED_HORIZ * fHoriz + + (ROOK_CONNECTED_HORIZ * fHoriz + ROOK_CONNECTED_VERT * FLIP(fHoriz)), "rook connected"); *puBit = ROOK_XRAY_BIT; @@ -2991,7 +2991,7 @@ Return value: } -static FLAG FASTCALL +static FLAG FASTCALL _WRookToFriendRook(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -3018,7 +3018,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pRook = pos->rgSquare[cRook].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(cRook)); ASSERT(IS_ROOK(pRook)); @@ -3033,7 +3033,7 @@ Return value: ROOK, c, pos->iScore[WHITE], - (ROOK_CONNECTED_HORIZ * fHoriz + + (ROOK_CONNECTED_HORIZ * fHoriz + ROOK_CONNECTED_VERT * FLIP(fHoriz)), "rook connected"); *puBit = ROOK_XRAY_BIT; @@ -3041,7 +3041,7 @@ Return value: } -static FLAG FASTCALL +static FLAG FASTCALL _ERookToFriendQueen(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -3066,7 +3066,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(IS_ROOK(pRook)); @@ -3078,7 +3078,7 @@ Return value: return(FALSE); } -static FLAG FASTCALL +static FLAG FASTCALL _EQueenToFriendBishop(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -3104,7 +3104,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pQueen = pos->rgSquare[cQueen].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(cQueen)); ASSERT(IS_QUEEN(pQueen)); @@ -3117,7 +3117,7 @@ Return value: } -static FLAG FASTCALL +static FLAG FASTCALL _EQueenToFriendRook(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -3143,7 +3143,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pQueen = pos->rgSquare[cQueen].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(cQueen)); ASSERT(IS_QUEEN(pQueen)); @@ -3156,7 +3156,7 @@ Return value: } -static FLAG FASTCALL +static FLAG FASTCALL _EQueenToFriendQueen(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -3182,7 +3182,7 @@ Return value: COOR cQueen = pos->cPiece; PIECE p = pos->rgSquare[c].pPiece; PIECE pQueen = pos->rgSquare[cQueen].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(cQueen)); ASSERT(IS_QUEEN(pQueen)); @@ -3196,7 +3196,7 @@ Return value: // ---------------------------------------------------------------------- -static FLAG FASTCALL +static FLAG FASTCALL _BMinorToEmpty(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -3221,7 +3221,7 @@ Return value: #ifdef DEBUG PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; PIECE p = pos->rgSquare[c].pPiece; - + ASSERT(IS_EMPTY(p)); ASSERT(!IS_EMPTY(pMinor)); ASSERT(IS_BISHOP(pMinor) || IS_KNIGHT(pMinor)); @@ -3232,7 +3232,7 @@ Return value: return(FALSE); } -static FLAG FASTCALL +static FLAG FASTCALL _WMinorToEmpty(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -3257,7 +3257,7 @@ Return value: #ifdef DEBUG PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; PIECE p = pos->rgSquare[c].pPiece; - + ASSERT(IS_EMPTY(p)); ASSERT(!IS_EMPTY(pMinor)); ASSERT(IS_BISHOP(pMinor) || IS_KNIGHT(pMinor)); @@ -3268,7 +3268,7 @@ Return value: return(FALSE); } -static FLAG FASTCALL +static FLAG FASTCALL _WRookToEmpty(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -3293,7 +3293,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(IS_ROOK(pRook)); @@ -3305,7 +3305,7 @@ Return value: return(FALSE); } -static FLAG FASTCALL +static FLAG FASTCALL _BRookToEmpty(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -3330,7 +3330,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(IS_ROOK(pRook)); @@ -3342,7 +3342,7 @@ Return value: return(FALSE); } -static FLAG FASTCALL +static FLAG FASTCALL _BQueenToEmpty(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -3367,7 +3367,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(IS_QUEEN(pQueen)); @@ -3379,7 +3379,7 @@ Return value: return(FALSE); } -static FLAG FASTCALL +static FLAG FASTCALL _WQueenToEmpty(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -3404,7 +3404,7 @@ Return value: #ifdef DEBUG PIECE p = pos->rgSquare[c].pPiece; PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(IS_QUEEN(pQueen)); @@ -3418,7 +3418,7 @@ Return value: -static FLAG FASTCALL +static FLAG FASTCALL _EBishopToFriendPawn(IN POSITION *pos, IN COOR c, IN OUT ULONG *puMobility, @@ -3443,7 +3443,7 @@ Return value: #ifdef DEBUG PIECE pPawn = pos->rgSquare[c].pPiece; PIECE pBishop = pos->rgSquare[pos->cPiece].pPiece; - + ASSERT(IS_ON_BOARD(c)); ASSERT(IS_ON_BOARD(pos->cPiece)); ASSERT(IS_BISHOP(pBishop)); @@ -3464,7 +3464,7 @@ Return value: static void _EvalBishop(IN OUT POSITION *pos, - IN COOR c, + IN COOR c, IN PAWN_HASH_ENTRY *pHash) /** @@ -3484,7 +3484,7 @@ Return value: { static const BITBOARD bbColorSq[2] = { 0x55aa55aa55aa55aaULL, 0xaa55aa55aa55aa55ULL }; - static const PMOBILITY_HELPER BMobJumpTable[2][14] = + static const PMOBILITY_HELPER BMobJumpTable[2][14] = { {// (black) _BMinorToEmpty, // EMPTY_SQUARE (0) @@ -3519,7 +3519,7 @@ Return value: _AnythingToFriendNoXray, // WHITE_KING (13) } }; - static const COOR cBishopAtHome[2][2] = + static const COOR cBishopAtHome[2][2] = { { C8, F8 }, // BLACK { C1, F1 } // WHITE @@ -3537,7 +3537,7 @@ Return value: ULONG uBit; PIECE p; PMOBILITY_HELPER pFun; - + ASSERT(IS_ON_BOARD(c)); p = pos->rgSquare[c].pPiece; ASSERT(p && IS_BISHOP(p)); @@ -3580,7 +3580,7 @@ Return value: "good/bad stationary pawns ++"); i = 0; - bbPc = ~(pHash->bbStationaryPawns[WHITE] | + bbPc = ~(pHash->bbStationaryPawns[WHITE] | pHash->bbStationaryPawns[BLACK]); bbPc &= bbMask; bb = pHash->bbPawnLocations[uColor] & bbPc; @@ -3604,7 +3604,7 @@ Return value: ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece)); ASSERT(GET_COLOR(pos->rgSquare[cSquare].pPiece) == FLIP(uColor)); ASSERT(pHash->bbPawnLocations[FLIP(uColor)] & COOR_TO_BB(cSquare)); - i += + i += (pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)].small.uPawn != 0) * TRANSIENT_PAWN_ON_BISHOP_COLOR[cSquare] / 2; } @@ -3625,7 +3625,7 @@ Return value: pos->iScore[uColor], BISHOP_IN_CLOSED_POSITION[pos->uClosedScaler], "in closed/open position"); - + // // Bishop mobility (and update attack table bits) // @@ -3637,7 +3637,7 @@ Return value: uCurrentMobility = 0; uBit = MINOR_BIT; cSquare = c + g_iBDeltas[u]; - + while(IS_ON_BOARD(cSquare)) { // @@ -3694,9 +3694,9 @@ Return value: uTotalMobility /= 2; ASSERT((pos->uMinMobility[uColor] & 0x80000000) == 0); ASSERT((uTotalMobility & 0x80000000) == 0); - pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor], + pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor], uTotalMobility); - + // // Look for "active bad bishops". See if square c is safe from // enemy pawns. @@ -3730,8 +3730,8 @@ Return value: } static void -_EvalKnight(IN OUT POSITION *pos, - IN COOR c, +_EvalKnight(IN OUT POSITION *pos, + IN COOR c, IN PAWN_HASH_ENTRY *pHash) /** @@ -3750,7 +3750,7 @@ Return value: **/ { static const int iPawnStart[2] = { -17, +15 }; - static const PMOBILITY_HELPER NMobJumpTable[2][14] = + static const PMOBILITY_HELPER NMobJumpTable[2][14] = { {// (black) _BMinorToEmpty, // EMPTY_SQUARE (0) @@ -3785,7 +3785,7 @@ Return value: _AnythingToFriendNoXray, // WHITE_KING (13) } }; - static const COOR cKnightAtHome[2][2] = + static const COOR cKnightAtHome[2][2] = { { B8, G8 }, // BLACK { B1, G1 } // WHITE @@ -3819,8 +3819,8 @@ Return value: // // TODO: Don't block unmoved E2/D2 pawns - // - + // + // // Centrality bonus // @@ -3859,13 +3859,13 @@ Return value: uPawnsSupporting = 0; p = BLACK_PAWN | uColor; cSquare = c + iPawnStart[uColor]; - uPawnsSupporting = (IS_ON_BOARD(cSquare) && + uPawnsSupporting = (IS_ON_BOARD(cSquare) && (pos->rgSquare[cSquare].pPiece == p)); cSquare += 2; uPawnsSupporting += (IS_ON_BOARD(cSquare) && (pos->rgSquare[cSquare].pPiece == p)); ASSERT(uPawnsSupporting <= 2); - + // // Give a bonus based on distance from enemy king // @@ -3877,7 +3877,7 @@ Return value: pos->iReducedMaterialDownScaler[uColor], i, "[scaled] unassailable/support"); - + // // Now, if there's zero or one supporter see if the enemy has a // bishop the color of the knight it could capture with. @@ -3898,7 +3898,7 @@ Return value: } else { - if (pos->uNonPawnCount[FLIP(uColor)][BISHOP] - + if (pos->uNonPawnCount[FLIP(uColor)][BISHOP] - pos->uWhiteSqBishopCount[FLIP(uColor)]) { EVAL_TERM(uColor, @@ -3910,7 +3910,7 @@ Return value: } } } - } + } else // not outposted { // @@ -3924,7 +3924,7 @@ Return value: i, "[scaled] enemy king tropism"); } - + // // Give a bonus for blockading an enemy backward pawn. Note: // we also reward pieces for blocking enemy passers in the @@ -3946,7 +3946,7 @@ Return value: // // A knight with an open file behind it is good. - // + // uPawnFile = FILE(c) + 1; EVAL_TERM(uColor, KNIGHT, @@ -3955,7 +3955,7 @@ Return value: ((pHash->uCountPerFile[uColor][uPawnFile] == 0) * KNIGHT_ON_INTERESTING_SQUARE_BY_RANK[uColor][RANK(c)]), "open file behind"); - + // // Do mobilility and piece relevance. Also update attack tables. // @@ -4007,13 +4007,13 @@ Return value: } ASSERT((pos->uMinMobility[uColor] & 0x80000000) == 0); ASSERT((uMobilitySquares & 0x80000000) == 0); - pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor], + pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor], uMobilitySquares); } static void -_EvalRook(IN OUT POSITION *pos, - IN COOR c, +_EvalRook(IN OUT POSITION *pos, + IN COOR c, IN PAWN_HASH_ENTRY *pHash) /** @@ -4031,7 +4031,7 @@ Return value: **/ { - static const PMOBILITY_HELPER RMobJumpTable[2][14] = + static const PMOBILITY_HELPER RMobJumpTable[2][14] = { {// (black) _BRookToEmpty, // EMPTY_SQUARE (0) @@ -4080,14 +4080,14 @@ Return value: ULONG uBit; PMOBILITY_HELPER pFun; SCORE i; - + ASSERT(IS_ON_BOARD(c)); p = pos->rgSquare[c].pPiece; ASSERT(p && IS_ROOK(p)); uColor = GET_COLOR(p); ASSERT(IS_VALID_COLOR(uColor)); pos->cPiece = c; - + // // Reward being on a half open or full open file. // @@ -4117,7 +4117,7 @@ Return value: pos->iScore[uColor], ROOK_ON_HALF_OPEN_WITH_ENEMY_BY_DIST_FROM_EKING[u], "on half open"); - + // // Added bonus if the enemy pawn is a passer. // @@ -4126,7 +4126,7 @@ Return value: { cSquare = CoorFromBitBoardRank8ToRank1(&bb); EVAL_TERM(uColor, - ROOK, + ROOK, c, pos->iScore[uColor], PASSER_BY_RANK[FLIP(uColor)][RANK(cSquare)] / 4, @@ -4160,7 +4160,7 @@ Return value: // while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb))) { - if (uColor == WHITE) + if (uColor == WHITE) { if (cSquare < c) { @@ -4208,22 +4208,22 @@ Return value: } } } - + // // Look for rooks that are trapping enemy kings. - // - if (uColor == WHITE) + // + if (uColor == WHITE) { pFriendRook = WHITE_ROOK; // // Rook on 7th/8th rank with an enemy king back there. - // + // if ((c < A6) && (pos->cNonPawns[BLACK][0] < A6)) { ASSERT(RANK(c) > 6); ASSERT(RANK(pos->cNonPawns[BLACK][0]) > 6); - + EVAL_TERM(WHITE, ROOK, c, @@ -4232,13 +4232,13 @@ Return value: "rook trapping enemy king"); } } - else + else { pFriendRook = BLACK_ROOK; // // Rook on 7th/8th rank with an enemy king back there. - // + // if ((c > H3) && (pos->cNonPawns[WHITE][0] > H3)) { ASSERT(RANK(c) < 3); @@ -4251,17 +4251,17 @@ Return value: "rook trapping enemy king"); } } - + // // Rooks increase in value as pawns come off: - // + // // "A further refinement would be to raise the knight's value by // 1/16 and lower the rook's value by 1/8 for each pawn above five // of the side being valued, with the opposite adjustment for each // pawn short of five." // --Larry Kaufman, IM // "Evaluation of Material Imbalance" - // + // EVAL_TERM(uColor, ROOK, c, @@ -4269,7 +4269,7 @@ Return value: ROOK_VALUE_AS_PAWNS_COME_OFF[pos->uPawnCount[uColor] + pos->uPawnCount[FLIP(uColor)]], "increase in value/pawns"); - + // // Rook mobility // @@ -4280,7 +4280,7 @@ Return value: uCurrentMobility = 0; uBit = ROOK_BIT; cSquare = c + g_iRDeltas[u]; - + while(IS_ON_BOARD(cSquare)) { // @@ -4334,7 +4334,7 @@ Return value: ASSERT((pos->uMinMobility[uColor] & 0x80000000) == 0); ASSERT((uTotalMobility & 0x80000000) == 0); - pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor], + pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor], uTotalMobility); if (uTotalMobility < 3) { @@ -4349,7 +4349,7 @@ Return value: // // Rook trapped in the corner by a stupid friendly king? - // + // ASSERT(IS_VALID_COLOR(uColor)); if (uColor == WHITE) { @@ -4373,7 +4373,7 @@ Return value: } } } - else + else { ASSERT(uColor == BLACK); ASSERT(IS_ON_BOARD(c)); @@ -4381,7 +4381,7 @@ Return value: { cSquare = pos->cNonPawns[BLACK][0]; ASSERT(IS_ON_BOARD(cSquare)); - + if (RANK8(cSquare)) { if (((cSquare > E8) && (c > cSquare)) || @@ -4402,8 +4402,8 @@ Return value: static void -_EvalQueen(IN OUT POSITION *pos, - IN COOR c, +_EvalQueen(IN OUT POSITION *pos, + IN COOR c, IN PAWN_HASH_ENTRY *pHash) /** @@ -4419,7 +4419,7 @@ Return value: **/ { - static const PMOBILITY_HELPER QMobJumpTable[2][14] = + static const PMOBILITY_HELPER QMobJumpTable[2][14] = { { _BQueenToEmpty, // EMPTY_SQUARE (0) @@ -4473,12 +4473,12 @@ Return value: cKing = pos->cNonPawns[FLIP(uColor)][0]; ASSERT(IS_ON_BOARD(cKing)); ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); - + if ((FALSE == pos->fCastled[uColor]) && (pos->uMinorsAtHome[uColor] > 1)) { // // Discourage queen being out too early - // + // if (!RANK1(c) && !RANK8(c)) { ASSERT(QUEEN_OUT_EARLY[pos->uMinorsAtHome[uColor]] < 0); @@ -4522,7 +4522,7 @@ Return value: // Toggle attack table bits. // pos->rgSquare[cSquare|8].bvAttacks[uColor].uWholeThing |= uBit; - + // // What did we hit? // @@ -4560,7 +4560,7 @@ Return value: } ASSERT((pos->uMinMobility[uColor] & 0x80000000) == 0); ASSERT((uTotalMobility & 0x80000000) == 0); - pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor], + pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor], uTotalMobility); // @@ -4576,8 +4576,8 @@ Return value: } static void -_EvalKing(IN OUT POSITION *pos, - IN const COOR c, +_EvalKing(IN OUT POSITION *pos, + IN const COOR c, IN const PAWN_HASH_ENTRY *pHash) /** @@ -4613,27 +4613,27 @@ Return value: { +4, +2, +1, 0, 0, 0, 0, 0, 0 }; - + static ULONG KingStormingPawnDefects[8] = { +0, +4, +3, +1, +0, +0, +0, +0 }; - static ULONG KingFileDefects[3] = + static ULONG KingFileDefects[3] = { +2, +1, +0 }; - static INT KingSafetyDeltas[11] = + static INT KingSafetyDeltas[11] = { -17, -16, -15, -1, +1, +15, +16, +17, -2, +2, 0 }; - - static ULONG EmptyAttackedSquare[2][11] = + + static ULONG EmptyAttackedSquare[2][11] = { - { + { 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 0 @@ -4645,9 +4645,9 @@ Return value: }, }; - static ULONG EmptyUnattackedSquare[2][11] = + static ULONG EmptyUnattackedSquare[2][11] = { - { + { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 @@ -4658,20 +4658,20 @@ Return value: 0, 0, 0, 0, 0, 0 }, }; - - static ULONG OccupiedAttackedSquare[2] = + + static ULONG OccupiedAttackedSquare[2] = { 1, 2 }; - + ASSERT(IS_ON_BOARD(c)); p = pos->rgSquare[c].pPiece; ASSERT(p && IS_KING(p)); uColor = GET_COLOR(p); ufColor = FLIP(uColor); ASSERT(IS_VALID_COLOR(uColor)); - + u = pos->uNonPawnMaterial[ufColor]; ASSERT(u >= VALUE_KING); if (u < DO_KING_SAFETY_THRESHOLD) @@ -4692,7 +4692,7 @@ Return value: } // Prepare to do king safety - uCounter = (u >= KEEP_KING_AT_HOME_THRESHOLD) * + uCounter = (u >= KEEP_KING_AT_HOME_THRESHOLD) * KING_INITIAL_COUNTER_BY_LOCATION[uColor][c]; #ifdef EVAL_DUMP Trace("%s Initial KS Counter: %u\n", COLOR_NAME(uColor), uCounter); @@ -4700,7 +4700,7 @@ Return value: uCounter += pos->uPiecesPointingAtKing[uColor] / 2; #ifdef EVAL_DUMP - Trace("%s KS Counter after pieces pointing: %u\n", + Trace("%s KS Counter after pieces pointing: %u\n", COLOR_NAME(uColor), uCounter); #endif uFlightSquares = 0; @@ -4712,13 +4712,13 @@ Return value: if (IS_ON_BOARD(cSquare)) { p = pos->rgSquare[cSquare].pPiece; - + cSquare |= 8; bvAttack = pos->rgSquare[cSquare].bvAttacks[ufColor].uSmall; bvXray = pos->rgSquare[cSquare].bvAttacks[ufColor].uXray; pos->rgSquare[cSquare].bvAttacks[uColor].small.uKing = 1; bvDefend = pos->rgSquare[cSquare].bvAttacks[uColor].uSmall; - + if (bvAttack != 0) { bvPattern |= (bvAttack | bvXray); @@ -4733,7 +4733,7 @@ Return value: } else { - uCounter += + uCounter += OccupiedAttackedSquare[OPPOSITE_COLORS(p, uColor)]; } uCounter += (bvDefend == 0); @@ -4779,7 +4779,7 @@ Return value: uCounter += KingStormingPawnDefects[DISTANCE(cSquare, c)]; } } - + u = FILE(c) + 1; v = (pHash->uCountPerFile[WHITE][u] > 0) + (pHash->uCountPerFile[BLACK][u] > 0); @@ -4809,10 +4809,10 @@ Return value: v = (pHash->uCountPerFile[WHITE][u] > 0) + (pHash->uCountPerFile[BLACK][u] > 0); ASSERT((v >= 0) && (v <= 2)); - + // Note: open rook files are worse than open interior files uCounter += (KingFileDefects[v] + ((v < 2) && ((u == 1) || (u == 8)))); - + bb = pHash->bbPawnLocations[ufColor] & BBFILE[u - 1]; if (bb) { @@ -4827,9 +4827,9 @@ Return value: ASSERT(IS_ON_BOARD(cSquare)); uCounter += KingStormingPawnDefects[DISTANCE(cSquare, c)]; } - } + } #ifdef EVAL_DUMP - Trace("%s KS Counter post-open file/stormers: %u\n", COLOR_NAME(uColor), + Trace("%s KS Counter post-open file/stormers: %u\n", COLOR_NAME(uColor), uCounter); #endif @@ -4839,10 +4839,10 @@ Return value: v = KING_COUNTER_BY_ATTACK_PATTERN[bvPattern]; uCounter += v; #ifdef EVAL_DUMP - Trace("%s KS Counter post-attack pattern: %u\n", COLOR_NAME(uColor), + Trace("%s KS Counter post-attack pattern: %u\n", COLOR_NAME(uColor), uCounter); #endif - + ASSERT(uFlightSquares <= 8); uCounter += KingFlightDefects[uFlightSquares] * (v > 1); #ifdef EVAL_DUMP @@ -4916,11 +4916,11 @@ Return value: } } } - + skip_safety: // // Special code for kings in the endgame - // + // u = pos->uNonPawnCount[WHITE][0] + pos->uNonPawnCount[BLACK][0]; if (u < 8) { @@ -4937,9 +4937,9 @@ Return value: // // Kings in front of passers in the late endgame are strong... - // + // cSquare = FILE(c); - bb = pHash->bbPasserLocations[uColor] & + bb = pHash->bbPasserLocations[uColor] & (BBADJACENT_FILES[cSquare] | BBFILE[cSquare]); if (bb) { @@ -4949,7 +4949,7 @@ Return value: { ASSERT(abs((INT)FILE(c) - (INT)FILE(cSquare)) <= 1); ASSERT(abs((INT)RANK(c) - (INT)RANK(cSquare)) <= 1); - + i = 2; i += SUPPORTED_PASSER_BY_RANK[uColor][RANK(cSquare)]; switch(uColor) @@ -4957,7 +4957,7 @@ Return value: case WHITE: i += 8 * (c < (cSquare - 1)); break; - + case BLACK: i += 8 * (c > (cSquare + 1)); break; @@ -4971,12 +4971,12 @@ Return value: } } } - + // // Detect kings way out of the action in KP endgames: - // + // // 8/8/1p6/p6k/P3K3/8/1P6/8 w - - 0 11 - // + // if ((u == 2) && (uColor == WHITE)) { i = 0; @@ -5001,7 +5001,7 @@ Return value: FLAG -EvalPasserRaces(IN OUT POSITION *pos, +EvalPasserRaces(IN OUT POSITION *pos, IN PAWN_HASH_ENTRY *pHash) /** @@ -5074,24 +5074,24 @@ Return value: uPawnDist--; } ASSERT((uPawnDist >= 1) && (uPawnDist <= 6)); - + uKingDist = DISTANCE(cKing, cQueen); - if (uKingDist > 0) + if (uKingDist > 0) { uKingDist -= (pos->uToMove == FLIP(uColor)); } ASSERT((uKingDist >= 0) && (uPawnDist <= 7)); - + if (uPawnDist < uKingDist) { uRacerDist[uColor] = MINU(uRacerDist[uColor], uPawnDist); } - + cKing = pos->cNonPawns[uColor][0]; ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == uColor); - + uFriendDist = DISTANCE(cKing, cQueen); if ((uFriendDist <= 1) && (DISTANCE(cKing, c) <= 1)) { @@ -5108,8 +5108,8 @@ Return value: } } fDontCountMeOut[uColor] = TRUE; - - // + + // // TODO: winning connected passers // } @@ -5117,7 +5117,7 @@ Return value: } } - if ((uRacerDist[WHITE] < uRacerDist[BLACK]) && + if ((uRacerDist[WHITE] < uRacerDist[BLACK]) && (FALSE == fDontCountMeOut[BLACK])) { EVAL_TERM(WHITE, @@ -5143,8 +5143,8 @@ Return value: } -static void -_EvalPassers(IN OUT POSITION *pos, +static void +_EvalPassers(IN OUT POSITION *pos, IN PAWN_HASH_ENTRY *pHash) /** @@ -5173,7 +5173,7 @@ Return value: ASSERT(pHash->bbPasserLocations[WHITE] | pHash->bbPasserLocations[BLACK]); - + FOREACH_COLOR(uColor) { bb = pHash->bbPasserLocations[uColor]; @@ -5183,7 +5183,7 @@ Return value: ASSERT(uNumPassers[uColor] > 0); d1 = 16 * g_iAhead[uColor]; ASSERT((d1 * 2) == (32 * g_iAhead[uColor])); - + while(IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bb))) { // @@ -5222,23 +5222,23 @@ Return value: ASSERT(u == uColor); } #endif - + // // Consider distance from friend king / enemy king // i = (8 - DISTANCE(c, pos->cNonPawns[uColor][0])); - if (((uColor == WHITE) && + if (((uColor == WHITE) && ((pos->cNonPawns[BLACK][0] >> 4) <= (c >> 4))) || ((uColor == BLACK) && ((pos->cNonPawns[WHITE][0] >> 4) >= (c >> 4)))) { - // + // // The enemy king is ahead of the passer // i += FILE_DISTANCE(c, pos->cNonPawns[FLIP(uColor)][0]) * 4; } else { - // + // // The enemy king is behind the passer // i += 10 + DISTANCE(c, pos->cNonPawns[FLIP(uColor)][0]) * 4; @@ -5251,12 +5251,12 @@ Return value: pos->iScore[uColor], i, "passer distance to kings"); - + // // If a side is a piece down then the other side's passers // are even more dangerous. // - if (pos->uNonPawnCount[uColor][0] > + if (pos->uNonPawnCount[uColor][0] > pos->uNonPawnCount[FLIP(uColor)][0]) { i = (PASSER_BY_RANK[uColor][RANK(c)] / 2); @@ -5274,10 +5274,10 @@ Return value: } } } - + // // Game stage bonus - // + // u = pos->uArmyScaler[BLACK]; EVAL_TERM(WHITE, PAWN, @@ -5297,7 +5297,7 @@ Return value: } -static void +static void _EvalBadTrades(IN OUT POSITION *pos) /** @@ -5315,7 +5315,7 @@ Return value: { ULONG uAhead, uBehind; ULONG uMagnitude; - + // // See who is ahead // @@ -5335,7 +5335,7 @@ Return value: ASSERT(uBehind == WHITE); } #endif - uMagnitude = ((pos->uNonPawnMaterial[uAhead] + + uMagnitude = ((pos->uNonPawnMaterial[uAhead] + pos->uNonPawnCount[uAhead][0] * 128) - (pos->uNonPawnMaterial[uBehind] + pos->uNonPawnCount[uBehind][0] * 128)); @@ -5384,9 +5384,9 @@ Routine description: which the attack table was generated, we will have missed situations where a piece is undefended (or underdefended) and attacked by an enemy piece that is the same (or greater) value. - + Note: This routine only considers side to move. - + Also Note: this routine does not find pawns that are en prise, only pieces. @@ -5397,7 +5397,7 @@ Parameters: Return value: void - + **/ { COOR c; @@ -5428,7 +5428,7 @@ Return value: } -static void +static void _EvalTrappedPieces(IN OUT SEARCHER_THREAD_CONTEXT *ctx) /** @@ -5530,9 +5530,9 @@ Return value: -SCORE -Eval(IN SEARCHER_THREAD_CONTEXT *ctx, - IN SCORE iAlpha, +SCORE +Eval(IN SEARCHER_THREAD_CONTEXT *ctx, + IN SCORE iAlpha, IN SCORE iBeta) /** @@ -5585,28 +5585,28 @@ Return value: ASSERT(IS_VALID_SCORE(iAlpha)); ASSERT(IS_VALID_SCORE(iBeta)); ASSERT(iAlpha < iBeta); - ASSERT((pos->iMaterialBalance[WHITE] * -1) == + ASSERT((pos->iMaterialBalance[WHITE] * -1) == pos->iMaterialBalance[BLACK]); // ASSERT(!InCheck(pos, pos->uToMove)); pos->uMinMobility[BLACK] = pos->uMinMobility[WHITE] = 100; pos->cTrapped[BLACK] = pos->cTrapped[WHITE] = ILLEGAL_COOR; - + #ifdef EVAL_HASH // // Check the eval hash - // + // iScoreForSideToMove = ProbeEvalHash(ctx, iAlpha, iBeta); - if (iScoreForSideToMove != INVALID_SCORE) + if (iScoreForSideToMove != INVALID_SCORE) { INC(ctx->sCounters.tree.u64EvalHashHits); goto end; } #endif - pos->iScore[BLACK] = + pos->iScore[BLACK] = (pos->uPawnMaterial[BLACK] + pos->uNonPawnMaterial[BLACK]); - pos->iScore[WHITE] = + pos->iScore[WHITE] = (pos->uPawnMaterial[WHITE] + pos->uNonPawnMaterial[WHITE]); #ifdef EVAL_DUMP EvalTraceClear(); @@ -5629,21 +5629,21 @@ Return value: #ifdef EVAL_DUMP Trace("After pawns:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]); #endif - + // // Look for won passers races. Note: This code cannot trust the // state of the attack tables yet! // - if ((pos->uNonPawnCount[WHITE][0] == 1) || + if ((pos->uNonPawnCount[WHITE][0] == 1) || (pos->uNonPawnCount[BLACK][0] == 1)) { (void)EvalPasserRaces(pos, pHash); #ifdef EVAL_DUMP - Trace("After passer races:\n%d\t\t%d\n", pos->iScore[WHITE], + Trace("After passer races:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]); #endif } - + // // Bad trade code. Right. Note: this code cannot trust the state // of the attack tables... @@ -5652,7 +5652,7 @@ Return value: { _EvalBadTrades(pos); #ifdef EVAL_DUMP - Trace("After bad trades:\n%d\t\t%d\n", pos->iScore[WHITE], + Trace("After bad trades:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]); #endif } @@ -5663,7 +5663,7 @@ Return value: // _EvalBishopPairs(pos); #ifdef EVAL_DUMP - Trace("After bishop pair bonus:\n%d\t\t%d\n", pos->iScore[WHITE], + Trace("After bishop pair bonus:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]); #endif @@ -5671,7 +5671,7 @@ Return value: // // Compute an estimate of the score for the side to move based on the // eval terms we have already considered: - // + // // 1. Material balance // 2. Pawn structure bonuses/penalties (incl passers/candidates) // 3. Passer races are detected already @@ -5679,30 +5679,30 @@ Return value: // 5. We've already detected unwinnable endgames // 6. Bishop pairs // - iScoreForSideToMove = (pos->iScore[pos->uToMove] - + iScoreForSideToMove = (pos->iScore[pos->uToMove] - pos->iScore[FLIP(pos->uToMove)]); ASSERT(IS_VALID_SCORE(iScoreForSideToMove)); // // Eval has not considered several potentially large terms: - // + // // 1. Piece positional components (such as mobility, trapped) // 2. King safety penalties // 3. Other miscellaneous bonuses/penalties // // We build two "margins" to account for these components of the // score. - // + // iAlphaMargin = iBetaMargin = (50 + (SCORE)ctx->uPositional); - + // // If (score + alpha_margin) is already > alpha -OR- - // (score - beta_margin) is already < beta - // + // (score - beta_margin) is already < beta + // // ...then we can stop thinking about lazy eval; the rest of the // computation only increases the margin so we know LE will fail // and can save some work here. - // + // if ((iScoreForSideToMove + iAlphaMargin < iAlpha) || (iScoreForSideToMove - iBetaMargin > iBeta)) { @@ -5711,7 +5711,7 @@ Return value: // yet. So do the expensive part of lazy eval estimation and // widen the margin further for king safety issues and passed // pawns. - // + // _QuicklyEstimateKingSafetyTerm(pos, &iAlphaMargin, &iBetaMargin); _QuicklyEstimatePasserBonuses(pos, pHash, &iAlphaMargin, &iBetaMargin); @@ -5753,7 +5753,7 @@ Return value: { _PopulatePawnAttackBits(pos); } - + // // Pre-compute some common terms used in per-piece evals: // @@ -5771,7 +5771,7 @@ Return value: ASSERT(pos->uArmyScaler[BLACK] <= 31); ASSERT(pos->uArmyScaler[WHITE] >= 0); ASSERT(pos->uArmyScaler[WHITE] <= 31); - pos->iReducedMaterialDownScaler[BLACK] = + pos->iReducedMaterialDownScaler[BLACK] = pos->iReducedMaterialDownScaler[WHITE] = 0; // @@ -5804,7 +5804,7 @@ Return value: if (p & 0x4) { ASSERT(IS_BISHOP(p) || IS_KNIGHT(p)); - + // // Note: The side on move's pieces of value X are // evaluated and have their mobility counted and added to @@ -5826,7 +5826,7 @@ Return value: cDefer[uColor][IS_QUEEN(p)][uDefer[uColor][IS_QUEEN(p)]++] = c; } } - + uColor = FLIP(uColor); ASSERT(uColor != pos->uToMove); for (u = 1; @@ -5862,10 +5862,10 @@ Return value: cDefer[uColor][IS_QUEEN(p)][uDefer[uColor][IS_QUEEN(p)]++] = c; } } - + // // Evaluate any rook(s) for side on move then for side not on move. - // + // uColor = FLIP(uColor); for (u = 0; u < uDefer[uColor][0]; @@ -5882,7 +5882,7 @@ Return value: pos->iScore[WHITE], pos->iScore[BLACK]); #endif } - + uColor = FLIP(uColor); for (u = 0; u < uDefer[uColor][0]; @@ -5902,7 +5902,7 @@ Return value: // // Evaluate any queen(s) for side on move then for side not on move. - // + // uColor = FLIP(uColor); for (u = 0; u < uDefer[uColor][1]; @@ -5919,7 +5919,7 @@ Return value: pos->iScore[WHITE], pos->iScore[BLACK]); #endif } - + uColor = FLIP(uColor); for (u = 0; u < uDefer[uColor][1]; @@ -5936,7 +5936,7 @@ Return value: pos->iScore[WHITE], pos->iScore[BLACK]); #endif } - + // // Evaluate the two kings last. // @@ -5954,7 +5954,7 @@ Return value: #ifdef EVAL_DUMP Trace("After *k:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]); #endif - + c = pos->cNonPawns[WHITE][0]; #ifdef DEBUG ASSERT(IS_ON_BOARD(c)); @@ -5969,7 +5969,7 @@ Return value: #ifdef EVAL_DUMP Trace("After .k:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]); #endif - + // // Now that we have the whole attack table generated, think about // passed pawns identified by the pawn eval routine again. Also @@ -5980,7 +5980,7 @@ Return value: { _EvalPassers(pos, pHash); #ifdef EVAL_DUMP - Trace("After passers:\n%d\t\t%d\n", pos->iScore[WHITE], + Trace("After passers:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]); #endif } @@ -5998,7 +5998,7 @@ Return value: // // B over N in the endgame with 2 pawn wings. - // + // if ((pos->uNonPawnCount[WHITE][0] <= 2) && (pos->uNonPawnCount[BLACK][0] <= 2)) { @@ -6016,7 +6016,7 @@ Return value: pos->iScore[WHITE] /= 2; pos->iScore[BLACK] /= 2; } - } + } } else { @@ -6025,11 +6025,11 @@ Return value: // with two pawn wings where having a bishop is an // advantage. // - bb = (pHash->bbPawnLocations[WHITE] | + bb = (pHash->bbPawnLocations[WHITE] | pHash->bbPawnLocations[BLACK]); - ASSERT((BBFILE[A] | BBFILE[B] | BBFILE[C]) == + ASSERT((BBFILE[A] | BBFILE[B] | BBFILE[C]) == 0x0707070707070707ULL); - ASSERT((BBFILE[F] | BBFILE[G] | BBFILE[H]) == + ASSERT((BBFILE[F] | BBFILE[G] | BBFILE[H]) == 0xe0e0e0e0e0e0e0e0ULL); if ((bb & 0x0707070707070707ULL) && (bb & 0xe0e0e0e0e0e0e0e0ULL)) @@ -6059,7 +6059,7 @@ Return value: } } #ifdef EVAL_DUMP - Trace("After BOOC / BvsN:\n%d\t\t%d\n", + Trace("After BOOC / BvsN:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]); #endif } @@ -6068,12 +6068,12 @@ Return value: // Roll in the reduced material down scaler terms. // ASSERT(pos->iReducedMaterialDownScaler[BLACK] > -200); - ASSERT(pos->iReducedMaterialDownScaler[BLACK] < +200); + ASSERT(pos->iReducedMaterialDownScaler[BLACK] < +200); iAlphaMargin = (pos->iReducedMaterialDownScaler[BLACK] * (SCORE)REDUCED_MATERIAL_DOWN_SCALER[pos->uArmyScaler[BLACK]]) / 8; pos->iScore[BLACK] += iAlphaMargin; ASSERT(pos->iReducedMaterialDownScaler[WHITE] > -200); - ASSERT(pos->iReducedMaterialDownScaler[WHITE] < +200); + ASSERT(pos->iReducedMaterialDownScaler[WHITE] < +200); iAlphaMargin = (pos->iReducedMaterialDownScaler[WHITE] * (SCORE)REDUCED_MATERIAL_DOWN_SCALER[pos->uArmyScaler[WHITE]]) / 8; pos->iScore[WHITE] += iAlphaMargin; @@ -6081,13 +6081,13 @@ Return value: // // Almost done // - iScoreForSideToMove = (pos->iScore[pos->uToMove] - + iScoreForSideToMove = (pos->iScore[pos->uToMove] - pos->iScore[FLIP(pos->uToMove)]); #ifdef EVAL_DUMP - Trace("At the end:\n%d\t\t%d\n", pos->iScore[WHITE], + Trace("At the end:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]); #endif - + // // TODO: detect and discourage blocked positions? // @@ -6111,9 +6111,9 @@ Return value: ctx->uPositional = MAXU(ctx->uPositional, VALUE_PAWN); } g_Options.iLastEvalScore = iScoreForSideToMove; - + #ifdef EVAL_TIME - ctx->sCounters.tree.u64CyclesInEval += (SystemReadTimeStampCounter() - + ctx->sCounters.tree.u64CyclesInEval += (SystemReadTimeStampCounter() - uTimer); #endif INC(ctx->sCounters.tree.u64FullEvals); @@ -6127,7 +6127,7 @@ Return value: end: ASSERT(IS_VALID_SCORE(iScoreForSideToMove)); - ASSERT((iScoreForSideToMove > -NMATE) && + ASSERT((iScoreForSideToMove > -NMATE) && (iScoreForSideToMove < +NMATE)); ASSERT(abs(ctx->sPlyInfo[ctx->uPly].iKingScore[BLACK]) < 700); ASSERT(abs(ctx->sPlyInfo[ctx->uPly].iKingScore[WHITE]) < 700); diff --git a/src/fathom/LICENSE b/src/fathom/LICENSE new file mode 100644 index 0000000..274394e --- /dev/null +++ b/src/fathom/LICENSE @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) 2013-2018 Ronald de Man +Copyright (c) 2015 basil00 +Copyright (c) 2016-2025 by Jon Dart + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/src/fathom/stdendian.h b/src/fathom/stdendian.h new file mode 100644 index 0000000..9f598b5 --- /dev/null +++ b/src/fathom/stdendian.h @@ -0,0 +1,286 @@ +#ifndef _STDENDIAN_H_ +#define _STDENDIAN_H_ +/* from https://gist.github.com/michaeljclark/3b4fd912f6fa8bb598b3 */ +/* modified to use functions not macros for bswap */ +/* and added a fix for Cygwin */ +/* + * stdendian.h + * + * This header defines the following endian macros as defined here: + * http://austingroupbugs.net/view.php?id=162 + * + * BYTE_ORDER this macro shall have a value equal to one + * of the *_ENDIAN macros in this header. + * LITTLE_ENDIAN if BYTE_ORDER == LITTLE_ENDIAN, the host + * byte order is from least significant to + * most significant. + * BIG_ENDIAN if BYTE_ORDER == BIG_ENDIAN, the host byte + * order is from most significant to least + * significant. + * + * The following are defined as macros: + * + * uint16_t bswap16(uint16_t x); + * uint32_t bswap32(uint32_t x); + * uint64_t bswap64(uint64_t x); + + * uint16_t htobe16(uint16_t x); + * uint16_t htole16(uint16_t x); + * uint16_t be16toh(uint16_t x); + * uint16_t le16toh(uint16_t x); + * + * uint32_t htobe32(uint32_t x); + * uint32_t htole32(uint32_t x); + * uint32_t be32toh(uint32_t x); + * uint32_t le32toh(uint32_t x); + * + * uint64_t htobe64(uint64_t x); + * uint64_t htole64(uint64_t x); + * uint64_t be64toh(uint64_t x); + * uint64_t le64toh(uint64_t x); + * + * The header defines the following macro for OpenCL compatibility + * https://www.khronos.org/registry/cl/sdk/2.0/docs/man/xhtml/preprocessorDirectives.html + * + * __ENDIAN_LITTLE__ if BYTE_ORDER == LITTLE_ENDIAN then this + * macro is present for OpenCL compatibility + * + * The implementation provides a uniform interface to endian macros using only + * system headers on recent Linux, Darwin, FreeBSD, Solaris and Windows systems. + * + * This approach is intended to avoid the need for preflight configure scripts. + * An alternative approach would be to test compiler CPU architecture marcros. + * + * This header has had *limited* testing on recent C11/C++11 compilers and is + * based on the austin bug tracker interface, manpages, and headers present in + * Linux, FreeBSD, Windows, Solaris and Darwin. + * + * The header uses __builtin_bswapXX intrinsic with GCC/Clang (__GNUC__) on + * platforms that do not provide bswap16, bswap32, bswap64 (Darwin) + * + * Public Domain. + */ + +/* requires C11 or C++11 */ +#if defined (__cplusplus) +#include +#elif !defined (__OPENCL_VERSION__) +#include +#endif + +/* Linux / GLIBC */ +#if defined(__linux__) || defined(__GLIBC__) || defined(__CYGWIN__) +#include +#include +#define __ENDIAN_DEFINED 1 +#define __BSWAP_DEFINED 1 +#define __HOSTSWAP_DEFINED 1 +// NDK defines _BYTE_ORDER etc +#ifndef _BYTE_ORDER +#define _BYTE_ORDER __BYTE_ORDER +#define _LITTLE_ENDIAN __LITTLE_ENDIAN +#define _BIG_ENDIAN __BIG_ENDIAN +#endif +#define bswap16(x) bswap_16(x) +#define bswap32(x) bswap_32(x) +#define bswap64(x) bswap_64(x) +#endif /* __linux__ || __GLIBC__ */ + +/* BSD */ +#if defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__DragonFly__) || defined(__OpenBSD__) +#include +#define __ENDIAN_DEFINED 1 +#define __BSWAP_DEFINED 1 +#define __HOSTSWAP_DEFINED 1 +#endif /* BSD */ + +/* Solaris */ +#if defined (sun) +#include +/* sun headers don't set a value for _LITTLE_ENDIAN or _BIG_ENDIAN */ +#if defined(_LITTLE_ENDIAN) +#undef _LITTLE_ENDIAN +#define _LITTLE_ENDIAN 1234 +#define _BIG_ENDIAN 4321 +#define _BYTE_ORDER _LITTLE_ENDIAN +#elif defined(_BIG_ENDIAN) +#undef _BIG_ENDIAN +#define _LITTLE_ENDIAN 1234 +#define _BIG_ENDIAN 4321 +#define _BYTE_ORDER _BIG_ENDIAN +#endif +#define __ENDIAN_DEFINED 1 +#endif /* sun */ + +/* Windows */ +/* assumes all Microsoft targets are little endian. */ +/* Emscripten (emcc) also currently assumes little endian. */ +#if defined(_WIN32) || defined(_MSC_VER) || defined(__EMSCRIPTEN__) +#define _LITTLE_ENDIAN 1234 +#define _BIG_ENDIAN 4321 +#define _BYTE_ORDER _LITTLE_ENDIAN +#define __ENDIAN_DEFINED 1 +#endif /* _MSC_VER */ + +/* OS X */ +#if defined(__APPLE__) +#include +#define _BYTE_ORDER BYTE_ORDER +#define _LITTLE_ENDIAN LITTLE_ENDIAN +#define _BIG_ENDIAN BIG_ENDIAN +#define __ENDIAN_DEFINED 1 +#endif /* __APPLE__ */ + +/* OpenCL */ +#if defined (__OPENCL_VERSION__) +#define _LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#if defined (__ENDIAN_LITTLE__) +#define _BYTE_ORDER _LITTLE_ENDIAN +#else +#define _BYTE_ORDER _BIG_ENDIAN +#endif +#define bswap16(x) as_ushort(as_uchar2(ushort(x)).s1s0) +#define bswap32(x) as_uint(as_uchar4(uint(x)).s3s2s1s0) +#define bswap64(x) as_ulong(as_uchar8(ulong(x)).s7s6s5s4s3s2s1s0) +#define __ENDIAN_DEFINED 1 +#define __BSWAP_DEFINED 1 +#endif + +/* Unknown */ +#if !__ENDIAN_DEFINED +#error Could not determine CPU byte order +#endif + +/* POSIX - http://austingroupbugs.net/view.php?id=162 */ +#ifndef BYTE_ORDER +#define BYTE_ORDER _BYTE_ORDER +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN _LITTLE_ENDIAN +#endif +#ifndef BIG_ENDIAN +#define BIG_ENDIAN _BIG_ENDIAN +#endif + +/* OpenCL compatibility - define __ENDIAN_LITTLE__ on little endian systems */ +#if _BYTE_ORDER == _LITTLE_ENDIAN +#if !defined (__ENDIAN_LITTLE__) +#define __ENDIAN_LITTLE__ 1 +#endif +#endif + +/* Byte swap macros */ +#if !__BSWAP_DEFINED + +#ifndef bswap16 +/* handle missing __builtin_bswap16 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52624 */ +#if defined __GNUC__ +#define bswap16(x) __builtin_bswap16(x) +#else +inline uint16_t bswap16(uint16_t x) { + return (uint16_t)((((uint16_t) (x) & 0xff00) >> 8) | \ + (((uint16_t) (x) & 0x00ff) << 8)); +} +#endif +#endif + +#ifndef bswap32 +#if defined __GNUC__ +#define bswap32(x) __builtin_bswap32(x) +#else +inline uint32_t bswap32(uint32_t x) { + return (( x & 0xff000000) >> 24) | \ + (( x & 0x00ff0000) >> 8) | \ + (( x & 0x0000ff00) << 8) | \ + (( x & 0x000000ff) << 24); +} +#endif +#endif + +#ifndef bswap64 +#if defined __GNUC__ +#define bswap64(x) __builtin_bswap64(x) +#else +inline uint64_t bswap64(uint64_t x) { + return (( x & 0xff00000000000000ull) >> 56) | \ + (( x & 0x00ff000000000000ull) >> 40) | \ + (( x & 0x0000ff0000000000ull) >> 24) | \ + (( x & 0x000000ff00000000ull) >> 8) | \ + (( x & 0x00000000ff000000ull) << 8) | \ + (( x & 0x0000000000ff0000ull) << 24) | \ + (( x & 0x000000000000ff00ull) << 40) | \ + (( x & 0x00000000000000ffull) << 56); +} +#endif +#endif + +#endif + +/* Host swap macros */ +#ifndef __HOSTSWAP_DEFINED +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define htobe16(x) bswap16((x)) +#define htole16(x) ((uint16_t)(x)) +#define be16toh(x) bswap16((x)) +#define le16toh(x) ((uint16_t)(x)) + +#define htobe32(x) bswap32((x)) +#define htole32(x) ((uint32_t)(x)) +#define be32toh(x) bswap32((x)) +#define le32toh(x) ((uint32_t)(x)) + +#define htobe64(x) bswap64((x)) +#define htole64(x) ((uint64_t)(x)) +#define be64toh(x) bswap64((x)) +#define le64toh(x) ((uint64_t)(x)) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define htobe16(x) ((uint16_t)(x)) +#define htole16(x) bswap16((x)) +#define be16toh(x) ((uint16_t)(x)) +#define le16toh(x) bswap16((x)) + +#define htobe32(x) ((uint32_t)(x)) +#define htole32(x) bswap32((x)) +#define be32toh(x) ((uint32_t)(x)) +#define le64toh(x) bswap64((x)) + +#define htobe64(x) ((uint64_t)(x)) +#define htole64(x) bswap64((x)) +#define be64toh(x) ((uint64_t)(x)) +#define le32toh(x) bswap32((x)) +#endif +#endif + +/* + +#include +#include + +int main() +{ + +#if BYTE_ORDER == LITTLE_ENDIAN +printf("little endian\n"); +#endif + +#if BYTE_ORDER == BIG_ENDIAN +printf("big endian\n"); +#endif + +printf("bswap16(%04x) %04x\n", 0xf0e0, bswap16(0xf0e0)); +printf("htobe16(%04x) %04x\n", 0xf0e0, htobe16(0xf0e0)); +printf("htole16(%04x) %04x\n", 0xf0e0, htole16(0xf0e0)); + +printf("bswap32(%08x) %08x\n", 0xf0e0d0c0, bswap32(0xf0e0d0c0)); +printf("htobe32(%08x) %08x\n", 0xf0e0d0c0, htobe32(0xf0e0d0c0)); +printf("htole32(%08x) %08x\n", 0xf0e0d0c0, htole32(0xf0e0d0c0)); + +printf("bswap64(%016llx) %016llx\n", 0xf0e0d0c0b0a09080ULL, bswap64(0xf0e0d0c0b0a09080ULL)); +printf("htobe64(%016llx) %016llx\n", 0xf0e0d0c0b0a09080ULL, htobe64(0xf0e0d0c0b0a09080ULL)); +printf("htole64(%016llx) %016llx\n", 0xf0e0d0c0b0a09080ULL, htole64(0xf0e0d0c0b0a09080ULL)); +} + +*/ +#endif diff --git a/src/fathom/tbchess.c b/src/fathom/tbchess.c new file mode 100644 index 0000000..bca4d1a --- /dev/null +++ b/src/fathom/tbchess.c @@ -0,0 +1,1050 @@ +/* +Copyright (c) 2015 basil00 +Modifications Copyright (c) 2016-2020 by Jon Dart + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#define TB_PAWN 1 +#define TB_KNIGHT 2 +#define TB_BISHOP 3 +#define TB_ROOK 4 +#define TB_QUEEN 5 +#define TB_KING 6 + +#define TB_WPAWN TB_PAWN +#define TB_BPAWN (TB_PAWN | 8) + +#define WHITE_KING (TB_WPAWN + 5) +#define WHITE_QUEEN (TB_WPAWN + 4) +#define WHITE_ROOK (TB_WPAWN + 3) +#define WHITE_BISHOP (TB_WPAWN + 2) +#define WHITE_KNIGHT (TB_WPAWN + 1) +#define WHITE_PAWN TB_WPAWN +#define BLACK_KING (TB_BPAWN + 5) +#define BLACK_QUEEN (TB_BPAWN + 4) +#define BLACK_ROOK (TB_BPAWN + 3) +#define BLACK_BISHOP (TB_BPAWN + 2) +#define BLACK_KNIGHT (TB_BPAWN + 1) +#define BLACK_PAWN TB_BPAWN + +#define PRIME_WHITE_QUEEN 11811845319353239651ull +#define PRIME_WHITE_ROOK 10979190538029446137ull +#define PRIME_WHITE_BISHOP 12311744257139811149ull +#define PRIME_WHITE_KNIGHT 15202887380319082783ull +#define PRIME_WHITE_PAWN 17008651141875982339ull +#define PRIME_BLACK_QUEEN 15484752644942473553ull +#define PRIME_BLACK_ROOK 18264461213049635989ull +#define PRIME_BLACK_BISHOP 15394650811035483107ull +#define PRIME_BLACK_KNIGHT 13469005675588064321ull +#define PRIME_BLACK_PAWN 11695583624105689831ull + +#define BOARD_RANK_EDGE 0x8181818181818181ull +#define BOARD_FILE_EDGE 0xFF000000000000FFull +#define BOARD_EDGE (BOARD_RANK_EDGE | BOARD_FILE_EDGE) +#define BOARD_RANK_1 0x00000000000000FFull +#define BOARD_FILE_A 0x8080808080808080ull + +#define KEY_KvK 0 + +#define BEST_NONE 0xFFFF +#define SCORE_ILLEGAL 0x7FFF + +// Note: WHITE, BLACK values are reverse of Stockfish +#ifdef __cplusplus +namespace { +enum Color { BLACK, WHITE }; +enum PieceType { PAWN=1, KNIGHT, BISHOP, ROOK, QUEEN, KING }; +enum Piece { + W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, + B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING }; +#else +typedef enum Color { BLACK, WHITE } Color; +typedef enum PieceType { PAWN=1, KNIGHT, BISHOP, ROOK, QUEEN, KING } PieceType; +typedef enum Piece { + W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, + B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING +} Piece; +#endif + +static inline Color ColorOfPiece(int piece) { + return (Color)(!(piece >> 3)); +} + +static inline PieceType TypeOfPiece(int piece) { + return (PieceType)(piece & 7); +} + +typedef int32_t Value; + +typedef struct Pos +{ + uint64_t white; + uint64_t black; + uint64_t kings; + uint64_t queens; + uint64_t rooks; + uint64_t bishops; + uint64_t knights; + uint64_t pawns; + uint8_t rule50; + uint8_t ep; + bool turn; +} Pos; + +static inline uint64_t pieces_by_type(const Pos *pos, Color c, PieceType p) { + uint64_t mask = (c == WHITE) ? pos->white : pos->black; + switch(p) { + case PAWN: + return pos->pawns & mask; + case KNIGHT: + return pos->knights & mask; + case BISHOP: + return pos->bishops & mask; + case ROOK: + return pos->rooks & mask; + case QUEEN: + return pos->queens & mask; + case KING: + return pos->kings & mask; + default: + assert(0); + return 0; + } +} + +static const char piece_to_char[] = " PNBRQK pnbrqk"; + +// map upper-case characters to piece types +static PieceType char_to_piece_type(char c) { + for (int i = PAWN; i <= KING; i++) + if (c == piece_to_char[i]) { + return (PieceType)i; + } + return (PieceType)0; +} + +#define rank(s) ((s) >> 3) +#define file(s) ((s) & 0x07) +#define board(s) ((uint64_t)1 << (s)) +#define square(r, f) (8 * (r) + (f)) + +#ifdef TB_KING_ATTACKS +#define king_attacks(s) TB_KING_ATTACKS(s) +#define king_attacks_init() /* NOP */ +#else /* TB_KING_ATTACKS */ + +static uint64_t king_attacks_table[64]; + +#define king_attacks(s) king_attacks_table[(s)] + +static void king_attacks_init(void) +{ + for (unsigned s = 0; s < 64; s++) + { + unsigned r = rank(s); + unsigned f = file(s); + uint64_t b = 0; + if (r != 0 && f != 0) + b |= board(square(r-1, f-1)); + if (r != 0) + b |= board(square(r-1, f)); + if (r != 0 && f != 7) + b |= board(square(r-1, f+1)); + if (f != 7) + b |= board(square(r, f+1)); + if (r != 7 && f != 7) + b |= board(square(r+1, f+1)); + if (r != 7) + b |= board(square(r+1, f)); + if (r != 7 && f != 0) + b |= board(square(r+1, f-1)); + if (f != 0) + b |= board(square(r, f-1)); + king_attacks_table[s] = b; + } +} + +#endif /* TB_KING_ATTACKS */ + +#ifdef TB_KNIGHT_ATTACKS +#define knight_attacks(s) TB_KNIGHT_ATTACKS(s) +#define knight_attacks_init() /* NOP */ +#else /* TB_KNIGHT_ATTACKS */ + +static uint64_t knight_attacks_table[64]; + +#define knight_attacks(s) knight_attacks_table[(s)] + +static void knight_attacks_init(void) +{ + for (unsigned s = 0; s < 64; s++) + { + int r1, r = rank(s); + int f1, f = file(s); + uint64_t b = 0; + r1 = r-1; f1 = f-2; + if (r1 >= 0 && f1 >= 0) + b |= board(square(r1, f1)); + r1 = r-1; f1 = f+2; + if (r1 >= 0 && f1 <= 7) + b |= board(square(r1, f1)); + r1 = r-2; f1 = f-1; + if (r1 >= 0 && f1 >= 0) + b |= board(square(r1, f1)); + r1 = r-2; f1 = f+1; + if (r1 >= 0 && f1 <= 7) + b |= board(square(r1, f1)); + r1 = r+1; f1 = f-2; + if (r1 <= 7 && f1 >= 0) + b |= board(square(r1, f1)); + r1 = r+1; f1 = f+2; + if (r1 <= 7 && f1 <= 7) + b |= board(square(r1, f1)); + r1 = r+2; f1 = f-1; + if (r1 <= 7 && f1 >= 0) + b |= board(square(r1, f1)); + r1 = r+2; f1 = f+1; + if (r1 <= 7 && f1 <= 7) + b |= board(square(r1, f1)); + knight_attacks_table[s] = b; + } +} + +#endif /* TB_KNIGHT_ATTACKS */ + +#ifdef TB_BISHOP_ATTACKS +#define bishop_attacks(s, occ) TB_BISHOP_ATTACKS(s, occ) +#define bishop_attacks_init() /* NOP */ +#else /* TB_BISHOP_ATTACKS */ + +static uint64_t diag_attacks_table[64][64]; +static uint64_t anti_attacks_table[64][64]; + +static const unsigned square2diag_table[64] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, + 14, 0, 1, 2, 3, 4, 5, 6, + 13, 14, 0, 1, 2, 3, 4, 5, + 12, 13, 14, 0, 1, 2, 3, 4, + 11, 12, 13, 14, 0, 1, 2, 3, + 10, 11, 12, 13, 14, 0, 1, 2, + 9, 10, 11, 12, 13, 14, 0, 1, + 8, 9, 10, 11, 12, 13, 14, 0 +}; + +static const unsigned square2anti_table[64] = +{ + 8, 9, 10, 11, 12, 13, 14, 0, + 9, 10, 11, 12, 13, 14, 0, 1, + 10, 11, 12, 13, 14, 0, 1, 2, + 11, 12, 13, 14, 0, 1, 2, 3, + 12, 13, 14, 0, 1, 2, 3, 4, + 13, 14, 0, 1, 2, 3, 4, 5, + 14, 0, 1, 2, 3, 4, 5, 6, + 0, 1, 2, 3, 4, 5, 6, 7 +}; + +static const uint64_t diag2board_table[15] = +{ + 0x8040201008040201ull, + 0x0080402010080402ull, + 0x0000804020100804ull, + 0x0000008040201008ull, + 0x0000000080402010ull, + 0x0000000000804020ull, + 0x0000000000008040ull, + 0x0000000000000080ull, + 0x0100000000000000ull, + 0x0201000000000000ull, + 0x0402010000000000ull, + 0x0804020100000000ull, + 0x1008040201000000ull, + 0x2010080402010000ull, + 0x4020100804020100ull, +}; + +static const uint64_t anti2board_table[15] = +{ + 0x0102040810204080ull, + 0x0204081020408000ull, + 0x0408102040800000ull, + 0x0810204080000000ull, + 0x1020408000000000ull, + 0x2040800000000000ull, + 0x4080000000000000ull, + 0x8000000000000000ull, + 0x0000000000000001ull, + 0x0000000000000102ull, + 0x0000000000010204ull, + 0x0000000001020408ull, + 0x0000000102040810ull, + 0x0000010204081020ull, + 0x0001020408102040ull, +}; + +static inline size_t diag2index(uint64_t b) +{ + b *= 0x0101010101010101ull; + b >>= 56; + b >>= 1; + return (size_t)b; +} + +static inline size_t anti2index(uint64_t b) +{ + return diag2index(b); +} + +#define diag(s) square2diag_table[(s)] +#define anti(s) square2anti_table[(s)] +#define diag2board(d) diag2board_table[(d)] +#define anti2board(a) anti2board_table[(a)] + +static uint64_t bishop_attacks(unsigned sq, uint64_t occ) +{ + occ &= ~board(sq); + unsigned d = diag(sq), a = anti(sq); + uint64_t d_occ = occ & (diag2board(d) & ~BOARD_EDGE); + uint64_t a_occ = occ & (anti2board(a) & ~BOARD_EDGE); + size_t d_idx = diag2index(d_occ); + size_t a_idx = anti2index(a_occ); + uint64_t d_attacks = diag_attacks_table[sq][d_idx]; + uint64_t a_attacks = anti_attacks_table[sq][a_idx]; + return d_attacks | a_attacks; +} + +static void bishop_attacks_init(void) +{ + for (unsigned idx = 0; idx < 64; idx++) + { + unsigned idx1 = idx << 1; + for (unsigned s = 0; s < 64; s++) + { + int r = rank(s); + int f = file(s); + uint64_t b = 0; + for (int i = -1; f + i >= 0 && r + i >= 0; i--) + { + unsigned occ = (1 << (f + i)); + b |= board(square(r + i, f + i)); + if (idx1 & occ) + break; + } + for (int i = 1; f + i <= 7 && r + i <= 7; i++) + { + unsigned occ = (1 << (f + i)); + b |= board(square(r + i, f + i)); + if (idx1 & occ) + break; + } + diag_attacks_table[s][idx] = b; + } + } + + for (unsigned idx = 0; idx < 64; idx++) + { + unsigned idx1 = idx << 1; + for (unsigned s = 0; s < 64; s++) + { + int r = rank(s); + int f = file(s); + uint64_t b = 0; + for (int i = -1; f + i >= 0 && r - i <= 7; i--) + { + unsigned occ = (1 << (f + i)); + b |= board(square(r - i, f + i)); + if (idx1 & occ) + break; + } + for (int i = 1; f + i <= 7 && r - i >= 0; i++) + { + unsigned occ = (1 << (f + i)); + b |= board(square(r - i, f + i)); + if (idx1 & occ) + break; + } + anti_attacks_table[s][idx] = b; + } + } +} + +#endif /* TB_BISHOP_ATTACKS */ + +#ifdef TB_ROOK_ATTACKS +#define rook_attacks(s, occ) TB_ROOK_ATTACKS(s, occ) +#define rook_attacks_init() /* NOP */ +#else /* TB_ROOK_ATTACKS */ + +static uint64_t rank_attacks_table[64][64]; +static uint64_t file_attacks_table[64][64]; + +static inline size_t rank2index(uint64_t b, unsigned r) +{ + b >>= (8 * r); + b >>= 1; + return (size_t)b; +} + +static inline size_t file2index(uint64_t b, unsigned f) +{ + b >>= f; + b *= 0x0102040810204080ull; + b >>= 56; + b >>= 1; + return (size_t)b; +} + +#define rank2board(r) (0xFFull << (8 * (r))) +#define file2board(f) (0x0101010101010101ull << (f)) + +static uint64_t rook_attacks(unsigned sq, uint64_t occ) +{ + occ &= ~board(sq); + unsigned r = rank(sq), f = file(sq); + uint64_t r_occ = occ & (rank2board(r) & ~BOARD_RANK_EDGE); + uint64_t f_occ = occ & (file2board(f) & ~BOARD_FILE_EDGE); + size_t r_idx = rank2index(r_occ, r); + size_t f_idx = file2index(f_occ, f); + uint64_t r_attacks = rank_attacks_table[sq][r_idx]; + uint64_t f_attacks = file_attacks_table[sq][f_idx]; + return r_attacks | f_attacks; +} + +static void rook_attacks_init(void) +{ + for (unsigned idx = 0; idx < 64; idx++) + { + unsigned idx1 = idx << 1, occ; + for (int f = 0; f <= 7; f++) + { + uint64_t b = 0; + if (f > 0) + { + int i = f-1; + do + { + occ = (1 << i); + b |= board(square(0, i)); + i--; + } + while (!(idx1 & occ) && i >= 0); + } + if (f < 7) + { + int i = f+1; + do + { + occ = (1 << i); + b |= board(square(0, i)); + i++; + } + while (!(idx1 & occ) && i <= 7); + } + for (int r = 0; r <= 7; r++) + { + rank_attacks_table[square(r, f)][idx] = b; + b <<= 8; + } + } + } + for (unsigned idx = 0; idx < 64; idx++) + { + unsigned idx1 = idx << 1, occ; + for (int r = 0; r <= 7; r++) + { + uint64_t b = 0; + if (r > 0) + { + int i = r-1; + do + { + occ = (1 << i); + b |= board(square(i, 0)); + i--; + } + while (!(idx1 & occ) && i >= 0); + } + if (r < 7) + { + int i = r+1; + do + { + occ = (1 << i); + b |= board(square(i, 0)); + i++; + } + while (!(idx1 & occ) && i <= 7); + } + for (int f = 0; f <= 7; f++) + { + file_attacks_table[square(r, f)][idx] = b; + b <<= 1; + } + } + } +} + +#endif /* TB_ROOK_ATTACKS */ + +#ifdef TB_QUEEN_ATTACKS +#define queen_attacks(s, occ) TB_QUEEN_ATTACKS(s, occ) +#else /* TB_QUEEN_ATTACKS */ +#define queen_attacks(s, occ) \ + (rook_attacks((s), (occ)) | bishop_attacks((s), (occ))) +#endif /* TB_QUEEN_ATTACKS */ + +#ifdef TB_PAWN_ATTACKS +#define pawn_attacks(s, c) TB_PAWN_ATTACKS(s, c) +#define pawn_attacks_init() /* NOP */ +#else /* TB_PAWN_ATTACKS */ + +static uint64_t pawn_attacks_table[2][64]; + +#define pawn_attacks(s, c) pawn_attacks_table[(c)][(s)] + +static void pawn_attacks_init(void) +{ + for (unsigned s = 0; s < 64; s++) + { + int r = rank(s); + int f = file(s); + + uint64_t b = 0; + if (r != 7) + { + if (f != 0) + b |= board(square(r+1, f-1)); + if (f != 7) + b |= board(square(r+1, f+1)); + } + pawn_attacks_table[1][s] = b; + + b = 0; + if (r != 0) + { + if (f != 0) + b |= board(square(r-1, f-1)); + if (f != 7) + b |= board(square(r-1, f+1)); + } + pawn_attacks_table[0][s] = b; + } +} + +#endif /* TB_PAWN_ATTACKS */ + +/* + * Given a position, produce a 64-bit material signature key. + */ +static uint64_t calc_key(const Pos *pos, bool mirror) +{ + uint64_t white = pos->white, black = pos->black; + if (mirror) + { + uint64_t tmp = white; + white = black; + black = tmp; + } + return popcount(white & pos->queens) * PRIME_WHITE_QUEEN + + popcount(white & pos->rooks) * PRIME_WHITE_ROOK + + popcount(white & pos->bishops) * PRIME_WHITE_BISHOP + + popcount(white & pos->knights) * PRIME_WHITE_KNIGHT + + popcount(white & pos->pawns) * PRIME_WHITE_PAWN + + popcount(black & pos->queens) * PRIME_BLACK_QUEEN + + popcount(black & pos->rooks) * PRIME_BLACK_ROOK + + popcount(black & pos->bishops) * PRIME_BLACK_BISHOP + + popcount(black & pos->knights) * PRIME_BLACK_KNIGHT + + popcount(black & pos->pawns) * PRIME_BLACK_PAWN; +} + +// Produce a 64-bit material key corresponding to the material combination +// defined by pcs[16], where pcs[1], ..., pcs[6] are the number of white +// pawns, ..., kings and pcs[9], ..., pcs[14] are the number of black +// pawns, ..., kings. +static uint64_t calc_key_from_pcs(int *pcs, int mirror) +{ + mirror = (mirror? 8: 0); + return pcs[WHITE_QUEEN ^ mirror] * PRIME_WHITE_QUEEN + + pcs[WHITE_ROOK ^ mirror] * PRIME_WHITE_ROOK + + pcs[WHITE_BISHOP ^ mirror] * PRIME_WHITE_BISHOP + + pcs[WHITE_KNIGHT ^ mirror] * PRIME_WHITE_KNIGHT + + pcs[WHITE_PAWN ^ mirror] * PRIME_WHITE_PAWN + + pcs[BLACK_QUEEN ^ mirror] * PRIME_BLACK_QUEEN + + pcs[BLACK_ROOK ^ mirror] * PRIME_BLACK_ROOK + + pcs[BLACK_BISHOP ^ mirror] * PRIME_BLACK_BISHOP + + pcs[BLACK_KNIGHT ^ mirror] * PRIME_BLACK_KNIGHT + + pcs[BLACK_PAWN ^ mirror] * PRIME_BLACK_PAWN; +} + +// Produce a 64-bit material key corresponding to the material combination +// piece[0], ..., piece[num - 1], where each value corresponds to a piece +// (1-6 for white pawn-king, 9-14 for black pawn-king). +static uint64_t calc_key_from_pieces(uint8_t *piece, int num) +{ + uint64_t key = 0; + static const uint64_t keys[16] = {0,PRIME_WHITE_PAWN,PRIME_WHITE_KNIGHT, + PRIME_WHITE_BISHOP,PRIME_WHITE_ROOK, + PRIME_WHITE_QUEEN,0,0,PRIME_BLACK_PAWN, + PRIME_BLACK_KNIGHT,PRIME_BLACK_BISHOP, + PRIME_BLACK_ROOK,PRIME_BLACK_QUEEN,0}; + for (int i = 0; i < num; i++) { + assert(piece[i]<16); + key += keys[piece[i]]; + } + return key; +} + +#define make_move(promote, from, to) \ + ((((promote) & 0x7) << 12) | (((from) & 0x3F) << 6) | ((to) & 0x3F)) +#define move_from(move) \ + (((move) >> 6) & 0x3F) +#define move_to(move) \ + ((move) & 0x3F) +#define move_promotes(move) \ + (((move) >> 12) & 0x7) + +static inline int type_of_piece_moved(Pos *pos, TbMove move) { + for (int i = PAWN; i <= KING; i++) { + if ((pieces_by_type(pos,(Color)(pos->turn == WHITE),(PieceType)i) & board(move_from(move))) != 0) { + return i; + } + } + assert(0); + return 0; +} + +#define MAX_MOVES TB_MAX_MOVES +#define MOVE_STALEMATE 0xFFFF +#define MOVE_CHECKMATE 0xFFFE + +static TbMove *add_move(TbMove *moves, bool promotes, unsigned from, + unsigned to) +{ + if (!promotes) + *moves++ = make_move(TB_PROMOTES_NONE, from, to); + else + { + *moves++ = make_move(TB_PROMOTES_QUEEN, from, to); + *moves++ = make_move(TB_PROMOTES_KNIGHT, from, to); + *moves++ = make_move(TB_PROMOTES_ROOK, from, to); + *moves++ = make_move(TB_PROMOTES_BISHOP, from, to); + } + return moves; +} + +/* + * Generate all captures, including all underpomotions + */ +static TbMove *gen_captures(const Pos *pos, TbMove *moves) +{ + uint64_t occ = pos->white | pos->black; + uint64_t us = (pos->turn? pos->white: pos->black), + them = (pos->turn? pos->black: pos->white); + uint64_t b, att; + { + unsigned from = lsb(pos->kings & us); + assert(from < 64); + for (att = king_attacks(from) & them; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->queens; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = queen_attacks(from, occ) & them; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->rooks; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = rook_attacks(from, occ) & them; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->bishops; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = bishop_attacks(from, occ) & them; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->knights; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = knight_attacks(from) & them; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->pawns; b; b = poplsb(b)) + { + unsigned from = lsb(b); + att = pawn_attacks(from, pos->turn); + if (pos->ep != 0 && ((att & board(pos->ep)) != 0)) + { + unsigned to = pos->ep; + moves = add_move(moves, false, from, to); + } + for (att = att & them; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, + to); + } + } + return moves; +} + +/* + * Generate all moves. + */ +static TbMove *gen_moves(const Pos *pos, TbMove *moves) +{ + uint64_t occ = pos->white | pos->black; + uint64_t us = (pos->turn? pos->white: pos->black), + them = (pos->turn? pos->black: pos->white); + uint64_t b, att; + + { + unsigned from = lsb(pos->kings & us); + for (att = king_attacks(from) & ~us; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->queens; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = queen_attacks(from, occ) & ~us; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->rooks; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = rook_attacks(from, occ) & ~us; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->bishops; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = bishop_attacks(from, occ) & ~us; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->knights; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = knight_attacks(from) & ~us; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->pawns; b; b = poplsb(b)) + { + unsigned from = lsb(b); + unsigned next = from + (pos->turn? 8: -8); + att = pawn_attacks(from, pos->turn); + if (pos->ep != 0 && ((att & board(pos->ep)) != 0)) + { + unsigned to = pos->ep; + moves = add_move(moves, false, from, to); + } + att &= them; + if ((board(next) & occ) == 0) + { + att |= board(next); + unsigned next2 = from + (pos->turn? 16: -16); + if ((pos->turn? rank(from) == 1: rank(from) == 6) && + ((board(next2) & occ) == 0)) + att |= board(next2); + } + for (; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, + to); + } + } + return moves; +} + +/* + * Test if the given move is an en passant capture. + */ +static bool is_en_passant(const Pos *pos, TbMove move) +{ + uint16_t from = move_from(move); + uint16_t to = move_to(move); + uint64_t us = (pos->turn? pos->white: pos->black); + if (pos->ep == 0) + return false; + if (to != pos->ep) + return false; + if ((board(from) & us & pos->pawns) == 0) + return false; + return true; +} + + +/* + * Test if the given move is a capture. + */ +static bool is_capture(const Pos *pos, TbMove move) +{ + uint16_t to = move_to(move); + uint64_t them = (pos->turn? pos->black: pos->white); + return (them & board(to)) != 0 || is_en_passant(pos,move); +} + + +/* + * Test if the given position is legal. + * (Pawns on backrank? Can the king be captured?) + */ +static bool is_legal(const Pos *pos) +{ + uint64_t occ = pos->white | pos->black; + uint64_t us = (pos->turn? pos->black: pos->white), + them = (pos->turn? pos->white: pos->black); + uint64_t king = pos->kings & us; + if (!king) + return false; + unsigned sq = lsb(king); + if (king_attacks(sq) & (pos->kings & them)) + return false; + uint64_t ratt = rook_attacks(sq, occ); + uint64_t batt = bishop_attacks(sq, occ); + if (ratt & (pos->rooks & them)) + return false; + if (batt & (pos->bishops & them)) + return false; + if ((ratt | batt) & (pos->queens & them)) + return false; + if (knight_attacks(sq) & (pos->knights & them)) + return false; + if (pawn_attacks(sq, !pos->turn) & (pos->pawns & them)) + return false; + return true; +} + +/* + * Test if the king is in check. + */ +static bool is_check(const Pos *pos) +{ + uint64_t occ = pos->white | pos->black; + uint64_t us = (pos->turn? pos->white: pos->black), + them = (pos->turn? pos->black: pos->white); + uint64_t king = pos->kings & us; + assert(king != 0); + unsigned sq = lsb(king); + uint64_t ratt = rook_attacks(sq, occ); + uint64_t batt = bishop_attacks(sq, occ); + if (ratt & (pos->rooks & them)) + return true; + if (batt & (pos->bishops & them)) + return true; + if ((ratt | batt) & (pos->queens & them)) + return true; + if (knight_attacks(sq) & (pos->knights & them)) + return true; + if (pawn_attacks(sq, pos->turn) & (pos->pawns & them)) + return true; + return false; +} + +/* + * Test if the position is valid. + */ +static bool is_valid(const Pos *pos) +{ + if (popcount(pos->kings) != 2) + return false; + if (popcount(pos->kings & pos->white) != 1) + return false; + if (popcount(pos->kings & pos->black) != 1) + return false; + if ((pos->white & pos->black) != 0) + return false; + if ((pos->kings & pos->queens) != 0) + return false; + if ((pos->kings & pos->rooks) != 0) + return false; + if ((pos->kings & pos->bishops) != 0) + return false; + if ((pos->kings & pos->knights) != 0) + return false; + if ((pos->kings & pos->pawns) != 0) + return false; + if ((pos->queens & pos->rooks) != 0) + return false; + if ((pos->queens & pos->bishops) != 0) + return false; + if ((pos->queens & pos->knights) != 0) + return false; + if ((pos->queens & pos->pawns) != 0) + return false; + if ((pos->rooks & pos->bishops) != 0) + return false; + if ((pos->rooks & pos->knights) != 0) + return false; + if ((pos->rooks & pos->pawns) != 0) + return false; + if ((pos->bishops & pos->knights) != 0) + return false; + if ((pos->bishops & pos->pawns) != 0) + return false; + if ((pos->knights & pos->pawns) != 0) + return false; + if (pos->pawns & BOARD_FILE_EDGE) + return false; + if ((pos->white | pos->black) != + (pos->kings | pos->queens | pos->rooks | pos->bishops | pos->knights | + pos->pawns)) + return false; + return is_legal(pos); +} + +#define do_bb_move(b, from, to) \ + (((b) & (~board(to)) & (~board(from))) | \ + ((((b) >> (from)) & 0x1) << (to))) + +static bool do_move(Pos *pos, const Pos *pos0, TbMove move) +{ + unsigned from = move_from(move); + unsigned to = move_to(move); + unsigned promotes = move_promotes(move); + pos->turn = !pos0->turn; + pos->white = do_bb_move(pos0->white, from, to); + pos->black = do_bb_move(pos0->black, from, to); + pos->kings = do_bb_move(pos0->kings, from, to); + pos->queens = do_bb_move(pos0->queens, from, to); + pos->rooks = do_bb_move(pos0->rooks, from, to); + pos->bishops = do_bb_move(pos0->bishops, from, to); + pos->knights = do_bb_move(pos0->knights, from, to); + pos->pawns = do_bb_move(pos0->pawns, from, to); + pos->ep = 0; + if (promotes != TB_PROMOTES_NONE) + { + pos->pawns &= ~board(to); // Promotion + switch (promotes) + { + case TB_PROMOTES_QUEEN: + pos->queens |= board(to); break; + case TB_PROMOTES_ROOK: + pos->rooks |= board(to); break; + case TB_PROMOTES_BISHOP: + pos->bishops |= board(to); break; + case TB_PROMOTES_KNIGHT: + pos->knights |= board(to); break; + } + pos->rule50 = 0; + } + else if ((board(from) & pos0->pawns) != 0) + { + pos->rule50 = 0; // Pawn move + if (rank(from) == 1 && rank(to) == 3 && + (pawn_attacks(from+8, true) & pos0->pawns & pos0->black) != 0) + pos->ep = from+8; + else if (rank(from) == 6 && rank(to) == 4 && + (pawn_attacks(from-8, false) & pos0->pawns & pos0->white) != 0) + pos->ep = from-8; + else if (to == pos0->ep) + { + unsigned ep_to = (pos0->turn? to-8: to+8); + uint64_t ep_mask = ~board(ep_to); + pos->white &= ep_mask; + pos->black &= ep_mask; + pos->pawns &= ep_mask; + } + } + else if ((board(to) & (pos0->white | pos0->black)) != 0) + pos->rule50 = 0; // Capture + else + pos->rule50 = pos0->rule50 + 1; // Normal move + if (!is_legal(pos)) + return false; + return true; +} + +static bool legal_move(const Pos *pos, TbMove move) { + struct Pos pos1; + return do_move(&pos1, pos, move); +} + +/* + * Test if the king is in checkmate. + */ +static bool is_mate(const Pos *pos) +{ + if (!is_check(pos)) + return false; + uint16_t moves0[MAX_MOVES]; + uint16_t *moves = moves0; + uint16_t *end = gen_moves(pos, moves); + for (; moves < end; moves++) + { + Pos pos1; + if (do_move(&pos1, pos, *moves)) + return false; + } + return true; +} + +/* + * Generate all legal moves. + */ +static TbMove *gen_legal(const Pos *pos, TbMove *moves) +{ + TbMove pl_moves[TB_MAX_MOVES]; + TbMove *end = gen_moves(pos, pl_moves); + TbMove *results = moves; + for (TbMove *m = pl_moves; m < end; m++) { + if (legal_move(pos,*m)) { + *results++ = *m; + } + } + return results; +} + +#ifdef __cplusplus +}; +#endif + diff --git a/src/fathom/tbconfig.h b/src/fathom/tbconfig.h new file mode 100644 index 0000000..5836386 --- /dev/null +++ b/src/fathom/tbconfig.h @@ -0,0 +1,150 @@ +/* + * tbconfig.h + * (C) 2015 basil, all rights reserved, + * Modifications Copyright 2016-2017 Jon Dart + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef TBCONFIG_H +#define TBCONFIG_H + +/****************************************************************************/ +/* BUILD CONFIG: */ +/****************************************************************************/ + +/* + * Define TB_CUSTOM_POP_COUNT to override the internal popcount + * implementation. To do this supply a macro or function definition + * here: + */ +/* #define TB_CUSTOM_POP_COUNT(x) */ + +/* + * Define TB_CUSTOM_LSB to override the internal lsb + * implementation. To do this supply a macro or function definition + * here: + */ +/* #define TB_CUSTOM_LSB(x) */ + +/* + * Define TB_NO_STDINT if you do not want to use or it is not + * available. + */ +/* #define TB_NO_STDINT */ + +/* + * Define TB_NO_STDBOOL if you do not want to use or it is not + * available or unnecessary (e.g. C++). + */ +/* #define TB_NO_STDBOOL */ + +/* + * Define TB_NO_THREADS if your program is not multi-threaded. + */ +/* #define TB_NO_THREADS */ + +/* + * Define TB_NO_HELPER_API if you do not need the helper API. + */ +/* #define TB_NO_HELPER_API */ + +/* + * Define TB_NO_HW_POP_COUNT if there is no hardware popcount instruction. + * + * Note: if defined, TB_CUSTOM_POP_COUNT is always used in preference + * to any built-in popcount functions. + * + * If no custom popcount function is defined, and if the following + * define is not set, the code will attempt to use an available hardware + * popcnt (currently supported on x86_64 architecture only) and otherwise + * will fall back to a software implementation. + */ +/* #define TB_NO_HW_POP_COUNT */ + +/***************************************************************************/ +/* SCORING CONSTANTS */ +/***************************************************************************/ +/* + * Fathom can produce scores for tablebase moves. These depend on the + * value of a pawn, and the magnitude of mate scores. The following + * constants are representative values but will likely need + * modification to adapt to an engine's own internal score values. + */ +#define TB_VALUE_PAWN 100 /* value of pawn in endgame */ +#define TB_VALUE_MATE 32000 +#define TB_VALUE_INFINITE 32767 /* value above all normal score values */ +#define TB_VALUE_DRAW 0 +#define TB_MAX_MATE_PLY 255 + +/***************************************************************************/ +/* ENGINE INTEGRATION CONFIG */ +/***************************************************************************/ + +/* + * If you are integrating tbprobe into an engine, you can replace some of + * tbprobe's built-in functionality with that already provided by the engine. + * This is OPTIONAL. If no definition are provided then tbprobe will use its + * own internal defaults. That said, for engines it is generally a good idea + * to avoid redundancy. + */ + +/* + * Define TB_KING_ATTACKS(square) to return the king attacks bitboard for a + * king at `square'. + */ +/* #define TB_KING_ATTACKS(square) */ + +/* + * Define TB_KNIGHT_ATTACKS(square) to return the knight attacks bitboard for + * a knight at `square'. + */ +/* #define TB_KNIGHT_ATTACKS(square) */ + +/* + * Define TB_ROOK_ATTACKS(square, occ) to return the rook attacks bitboard + * for a rook at `square' assuming the given `occ' occupancy bitboard. + */ +/* #define TB_ROOK_ATTACKS(square, occ) */ + +/* + * Define TB_BISHOP_ATTACKS(square, occ) to return the bishop attacks bitboard + * for a bishop at `square' assuming the given `occ' occupancy bitboard. + */ +/* #define TB_BISHOP_ATTACKS(square, occ) */ + +/* + * Define TB_QUEEN_ATTACKS(square, occ) to return the queen attacks bitboard + * for a queen at `square' assuming the given `occ' occupancy bitboard. + * NOTE: If no definition is provided then tbprobe will use: + * TB_ROOK_ATTACKS(square, occ) | TB_BISHOP_ATTACKS(square, occ) + */ +/* #define TB_QUEEN_ATTACKS(square, occ) */ + +/* + * Define TB_PAWN_ATTACKS(square, color) to return the pawn attacks bitboard + * for a `color' pawn at `square'. + * NOTE: This definition must work for pawns on ranks 1 and 8. For example, + * a white pawn on e1 attacks d2 and f2. A black pawn on e1 attacks + * nothing. Etc. + * NOTE: This definition must not include en passant captures. + */ +/* #define TB_PAWN_ATTACKS(square, color) */ + +#endif diff --git a/src/fathom/tbprobe.c b/src/fathom/tbprobe.c new file mode 100644 index 0000000..5eac014 --- /dev/null +++ b/src/fathom/tbprobe.c @@ -0,0 +1,2715 @@ +/* +Copyright (c) 2013-2018 Ronald de Man +Copyright (c) 2015 basil00 +Modifications Copyright (c) 2016-2024 by Jon Dart + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#ifdef __cplusplus +#include +#else +#include +#endif +#include +#include +#include +#include +#ifdef TB_NO_STDBOOL +#typedef uint8 bool +#else +#include +#endif +#include "tbprobe.h" + +#define TB_PIECES 7 +#define TB_HASHBITS (TB_PIECES < 7 ? 11 : 12) +#define TB_MAX_PIECE (TB_PIECES < 7 ? 254 : 650) +#define TB_MAX_PAWN (TB_PIECES < 7 ? 256 : 861) +#define TB_MAX_SYMS 4096 + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#define SEP_CHAR ':' +#define FD int +#define FD_ERR -1 +typedef size_t map_t; +#else +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#define SEP_CHAR ';' +#define FD HANDLE +#define FD_ERR INVALID_HANDLE_VALUE +typedef HANDLE map_t; +#endif + +// This must be after the inclusion of Windows headers, because otherwise +// std::byte conflicts with "byte" in rpcndr.h . The error occurs if C++ +// standard is at lest 17, as std::byte was introduced in C++17. +#ifdef __cplusplus +using namespace std; +#endif + +#define DECOMP64 + +// Threading support +#ifndef TB_NO_THREADS +#if defined(__cplusplus) && (__cplusplus >= 201103L) + +#include +#define LOCK_T std::mutex +#define LOCK_INIT(x) +#define LOCK_DESTROY(x) +#define LOCK(x) x.lock() +#define UNLOCK(x) x.unlock() + +#else +#ifndef _WIN32 +#define LOCK_T pthread_mutex_t +#define LOCK_INIT(x) pthread_mutex_init(&(x), NULL) +#define LOCK_DESTROY(x) pthread_mutex_destroy(&(x)) +#define LOCK(x) pthread_mutex_lock(&(x)) +#define UNLOCK(x) pthread_mutex_unlock(&(x)) +#else +#define LOCK_T HANDLE +#define LOCK_INIT(x) do { x = CreateMutex(NULL, FALSE, NULL); } while (0) +#define LOCK_DESTROY(x) CloseHandle(x) +#define LOCK(x) WaitForSingleObject(x, INFINITE) +#define UNLOCK(x) ReleaseMutex(x) +#endif + +#endif +#else /* TB_NO_THREADS */ +#define LOCK_T int +#define LOCK_INIT(x) /* NOP */ +#define LOCK_DESTROY(x) /* NOP */ +#define LOCK(x) /* NOP */ +#define UNLOCK(x) /* NOP */ +#endif + +// population count implementation +#undef TB_SOFTWARE_POP_COUNT + +#if defined(TB_CUSTOM_POP_COUNT) +#define popcount(x) TB_CUSTOM_POP_COUNT(x) +#elif defined(TB_NO_HW_POP_COUNT) +#define TB_SOFTWARE_POP_COUNT +#elif defined (__GNUC__) && defined(__x86_64__) && defined(__SSE4_2__) +#include +#define popcount(x) (int)_mm_popcnt_u64((x)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) && defined(_M_AMD64) +#include +#define popcount(x) (int)_mm_popcnt_u64((x)) +#else +// try to use a builtin +#if defined (__has_builtin) +#if __has_builtin(__builtin_popcountll) +#define popcount(x) __builtin_popcountll((x)) +#else +#define TB_SOFTWARE_POP_COUNT +#endif +#else +#define TB_SOFTWARE_POP_COUNT +#endif +#endif + +#ifdef TB_SOFTWARE_POP_COUNT +// Not a recognized compiler/architecture that has popcount, and +// no builtin available: fall back to a software popcount. This one +// is still reasonably fast. +static inline unsigned tb_software_popcount(uint64_t x) +{ + x = x - ((x >> 1) & 0x5555555555555555ull); + x = (x & 0x3333333333333333ull) + ((x >> 2) & 0x3333333333333333ull); + x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0full; + return (x * 0x0101010101010101ull) >> 56; +} +#define popcount(x) tb_software_popcount(x) +#endif + +// LSB (least-significant bit) implementation +#ifdef TB_CUSTOM_LSB +#define lsb(b) TB_CUSTOM_LSB(b) +#else +#if defined(__GNUC__) +static inline unsigned lsb(uint64_t b) { + assert(b != 0); + return __builtin_ffsll(b)-1; +} +#elif defined(_MSC_VER) +static inline unsigned lsb(uint64_t b) { + assert(b != 0); + DWORD index; +#ifdef _WIN64 + _BitScanForward64(&index,b); + return (unsigned)index; +#else + if (b & 0xffffffffULL) { + _BitScanForward(&index,(unsigned long)(b & 0xffffffffULL)); + return (unsigned)index; + } + else { + _BitScanForward(&index,(unsigned long)(b >> 32)); + return 32 + (unsigned)index; + } +#endif +} +#else +/* not a compiler/architecture with recognized builtins */ +static uint32_t get_bit32(uint64_t x) { + return (uint32_t)(((int32_t)(x))&-((int32_t)(x))); +} +static const unsigned MAGIC32 = 0xe89b2be; +static const uint32_t MagicTable32[32] = {31,0,9,1,10,20,13,2,7,11,21,23,17,14,3,25,30,8,19,12,6,22,16,24,29,18,5,15,28,4,27,26}; +static unsigned lsb(uint64_t b) { + if (b & 0xffffffffULL) + return MagicTable32[(get_bit32(b & 0xffffffffULL)*MAGIC32)>>27]; + else + return MagicTable32[(get_bit32(b >> 32)*MAGIC32)>>27]+32; +} +#endif +#endif + +#define max(a,b) a > b ? a : b +#define min(a,b) a < b ? a : b + +#include "stdendian.h" + +#if _BYTE_ORDER == _BIG_ENDIAN + +/* (unused) +static uint64_t from_le_u64(uint64_t input) { + return bswap64(input); +} +*/ + +static uint32_t from_le_u32(uint32_t input) { + return bswap32(input); +} + +static uint16_t from_le_u16(uint16_t input) { + return bswap16(input); +} + +static uint64_t from_be_u64(uint64_t x) { + return x; +} + +static uint32_t from_be_u32(uint32_t x) { + return x; +} + +/* (unused) + static uint16_t from_be_u16(uint16_t x) { + return x; + }*/ + +#else + +/* (unused) +static uint64_t from_le_u64(uint64_t x) { + return x; +} +*/ + +static uint32_t from_le_u32(uint32_t x) { + return x; +} + +static uint16_t from_le_u16(uint16_t x) { + return x; +} + +static uint64_t from_be_u64(uint64_t input) { + return bswap64(input); +} + +static uint32_t from_be_u32(uint32_t input) { + return bswap32(input); +} + +/* (unused) +static uint16_t from_be_u16(const uint16_t input) { + return bswap16(input); +} +*/ + +#endif + +inline static uint32_t read_le_u32(void *p) +{ + // input may be unaligned, so read into stack allocated + // buffer, which should be aligned + // (prevents runtime errors from glibc) + unsigned char buffer[4]; + memcpy(buffer, p, 4); + return from_le_u32(*(uint32_t *)buffer); +} + +inline static uint16_t read_le_u16(void *p) +{ + // input may be unaligned, so read into stack allocated + // buffer, which should be aligned + // (prevents runtime errors from glibc) + unsigned char buffer[2]; + buffer[0] = *((unsigned char*)p); + buffer[1] = *(((unsigned char*)p)+1); + return from_le_u16(*(uint16_t *)buffer); +} + +static size_t file_size(FD fd) { +#ifdef _WIN32 + LARGE_INTEGER fileSize; + if (GetFileSizeEx(fd, &fileSize)==0) { + return 0; + } + return (size_t)fileSize.QuadPart; +#else + struct stat buf; + if (fstat(fd,&buf)) { + return 0; + } else { + return buf.st_size; + } +#endif +} + +#ifndef TB_NO_THREADS +static LOCK_T tbMutex; +#endif +static int initialized = 0; +static int numPaths = 0; +static char *pathString = NULL; +static char **paths = NULL; + +static FD open_tb(const char *str, const char *suffix) +{ + int i; + FD fd; + char *file; + + for (i = 0; i < numPaths; i++) { + file = (char*)malloc(strlen(paths[i]) + strlen(str) + + strlen(suffix) + 2); + strcpy(file, paths[i]); +#ifdef _WIN32 + strcat(file,"\\"); +#else + strcat(file,"/"); +#endif + strcat(file, str); + strcat(file, suffix); +#ifndef _WIN32 + fd = open(file, O_RDONLY); +#else +#ifdef _UNICODE + wchar_t ucode_name[4096]; + size_t len; + mbstowcs_s(&len, ucode_name, 4096, file, _TRUNCATE); + /* use FILE_FLAG_RANDOM_ACCESS because we are likely to access this file + randomly, so prefetch is not helpful. See + https://github.com/official-stockfish/Stockfish/pull/1829 */ + fd = CreateFile(ucode_name, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); +#else + fd = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); +#endif +#endif + free(file); + if (fd != FD_ERR) { + return fd; + } + } + return FD_ERR; +} + +static void close_tb(FD fd) +{ +#ifndef _WIN32 + close(fd); +#else + CloseHandle(fd); +#endif +} + +static void *map_file(FD fd, map_t *mapping) +{ +#ifndef _WIN32 + struct stat statbuf; + if (fstat(fd, &statbuf)) { + perror("fstat"); + close_tb(fd); + return NULL; + } + *mapping = statbuf.st_size; + void *data = mmap(NULL, statbuf.st_size, PROT_READ, + MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + perror("mmap"); + return NULL; + } +#ifdef POSIX_MADV_RANDOM + /* Advise the kernel that we are likely to access this data + region randomly, so prefetch is not helpful. See + https://github.com/official-stockfish/Stockfish/pull/1829 */ + posix_madvise(data, statbuf.st_size, POSIX_MADV_RANDOM); +#endif +#else + DWORD size_low, size_high; + size_low = GetFileSize(fd, &size_high); + HANDLE map = CreateFileMapping(fd, NULL, PAGE_READONLY, size_high, size_low, + NULL); + if (map == NULL) { + fprintf(stderr,"CreateFileMapping() failed, error = %lu.\n", GetLastError()); + return NULL; + } + *mapping = (map_t)map; + void *data = (void *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); + if (data == NULL) { + fprintf(stderr,"MapViewOfFile() failed, error = %lu.\n", GetLastError()); + } +#endif + return data; +} + +#ifndef _WIN32 +static void unmap_file(void *data, map_t size) +{ + if (!data) return; + if (munmap(data, size) != 0) { + perror("munmap"); + } +} +#else +static void unmap_file(void *data, map_t mapping) +{ + if (!data) return; + if (!UnmapViewOfFile(data)) { + fprintf(stderr, "unmap failed, error code %lu\n", GetLastError()); + } + if (!CloseHandle((HANDLE)mapping)) { + fprintf(stderr, "CloseHandle failed, error code %lu\n", GetLastError()); + } +} +#endif + +#define poplsb(x) ((x) & ((x) - 1)) + +int TB_MaxCardinality = 0, TB_MaxCardinalityDTM = 0; +unsigned TB_LARGEST = 0; +//extern int TB_CardinalityDTM; + +static const char *tbSuffix[] = { ".rtbw", ".rtbm", ".rtbz" }; +static uint32_t tbMagic[] = { 0x5d23e871, 0x88ac504b, 0xa50c66d7 }; + +enum { WDL, DTM, DTZ }; +enum { PIECE_ENC, FILE_ENC, RANK_ENC }; + +// Attack and move generation code +#include "tbchess.c" + +struct PairsData { + uint8_t *indexTable; + uint16_t *sizeTable; + uint8_t *data; + uint16_t *offset; + uint8_t *symLen; + uint8_t *symPat; + uint8_t blockSize; + uint8_t idxBits; + uint8_t minLen; + uint8_t constValue[2]; + uint64_t base[1]; +}; + +struct EncInfo { + struct PairsData *precomp; + size_t factor[TB_PIECES]; + uint8_t pieces[TB_PIECES]; + uint8_t norm[TB_PIECES]; +}; + +struct BaseEntry { + uint64_t key; + uint8_t *data[3]; + map_t mapping[3]; +#ifdef __cplusplus +#if __cplusplus >= 202002L + atomic ready[3]{false, false, false}; +#else + atomic ready[3]; +#endif +#else + atomic_bool ready[3]; +#endif + uint8_t num; + bool symmetric, hasPawns, hasDtm, hasDtz; + union { + bool kk_enc; + uint8_t pawns[2]; + }; + bool dtmLossOnly; +}; + +struct PieceEntry { + struct BaseEntry be; + struct EncInfo ei[5]; // 2 + 2 + 1 + uint16_t *dtmMap; + uint16_t dtmMapIdx[2][2]; + void *dtzMap; + uint16_t dtzMapIdx[4]; + uint8_t dtzFlags; +}; + +struct PawnEntry { + struct BaseEntry be; + struct EncInfo ei[24]; // 4 * 2 + 6 * 2 + 4 + uint16_t *dtmMap; + uint16_t dtmMapIdx[6][2][2]; + void *dtzMap; + uint16_t dtzMapIdx[4][4]; + uint8_t dtzFlags[4]; + bool dtmSwitched; +}; + +struct TbHashEntry { + uint64_t key; + struct BaseEntry *ptr; +#ifdef __cplusplus + atomic error; +#else + atomic_bool error; +#endif +}; + +static int tbNumPiece, tbNumPawn; +static int numWdl, numDtm, numDtz; + +static struct PieceEntry *pieceEntry; +static struct PawnEntry *pawnEntry; +static struct TbHashEntry tbHash[1 << TB_HASHBITS]; + +static void init_indices(void); + +// Forward declarations. These functions without the tb_ +// prefix take a pos structure as input. +static int probe_wdl(Pos *pos, int *success); +static int probe_dtz(Pos *pos, int *success); +static int root_probe_wdl(const Pos *pos, bool useRule50, struct TbRootMoves *rm); +static int root_probe_dtz(const Pos *pos, bool hasRepeated, bool useRule50, struct TbRootMoves *rm); +static uint16_t probe_root(Pos *pos, int *score, unsigned *results); + +unsigned tb_probe_wdl_impl( + uint64_t white, + uint64_t black, + uint64_t kings, + uint64_t queens, + uint64_t rooks, + uint64_t bishops, + uint64_t knights, + uint64_t pawns, + unsigned ep, + bool turn) +{ + Pos pos = + { + white, + black, + kings, + queens, + rooks, + bishops, + knights, + pawns, + 0, + (uint8_t)ep, + turn + }; + int success; + int v = probe_wdl(&pos, &success); + if (success == 0) + return TB_RESULT_FAILED; + return (unsigned)(v + 2); +} + +static unsigned dtz_to_wdl(int cnt50, int dtz) +{ + int wdl = 0; + if (dtz > 0) + wdl = (dtz + cnt50 <= 100? 2: 1); + else if (dtz < 0) + wdl = (-dtz + cnt50 <= 100? -2: -1); + return wdl + 2; +} + +unsigned tb_probe_root_impl( + uint64_t white, + uint64_t black, + uint64_t kings, + uint64_t queens, + uint64_t rooks, + uint64_t bishops, + uint64_t knights, + uint64_t pawns, + unsigned rule50, + unsigned ep, + bool turn, + unsigned *results) +{ + Pos pos = + { + white, + black, + kings, + queens, + rooks, + bishops, + knights, + pawns, + (uint8_t)rule50, + (uint8_t)ep, + turn + }; + int dtz; + if (!is_valid(&pos)) + return TB_RESULT_FAILED; + TbMove move = probe_root(&pos, &dtz, results); + if (move == 0) + return TB_RESULT_FAILED; + if (move == MOVE_CHECKMATE) + return TB_RESULT_CHECKMATE; + if (move == MOVE_STALEMATE) + return TB_RESULT_STALEMATE; + unsigned res = 0; + res = TB_SET_WDL(res, dtz_to_wdl(rule50, dtz)); + res = TB_SET_DTZ(res, (dtz < 0? -dtz: dtz)); + res = TB_SET_FROM(res, move_from(move)); + res = TB_SET_TO(res, move_to(move)); + res = TB_SET_PROMOTES(res, move_promotes(move)); + res = TB_SET_EP(res, is_en_passant(&pos, move)); + return res; +} + +int tb_probe_root_dtz( + uint64_t white, + uint64_t black, + uint64_t kings, + uint64_t queens, + uint64_t rooks, + uint64_t bishops, + uint64_t knights, + uint64_t pawns, + unsigned rule50, + unsigned castling, + unsigned ep, + bool turn, + bool hasRepeated, + bool useRule50, + struct TbRootMoves *results) { + Pos pos = + { + white, + black, + kings, + queens, + rooks, + bishops, + knights, + pawns, + (uint8_t)rule50, + (uint8_t)ep, + turn + }; + if (castling != 0) return 0; + return root_probe_dtz(&pos, hasRepeated, useRule50, results); +} + +int tb_probe_root_wdl( + uint64_t white, + uint64_t black, + uint64_t kings, + uint64_t queens, + uint64_t rooks, + uint64_t bishops, + uint64_t knights, + uint64_t pawns, + unsigned rule50, + unsigned castling, + unsigned ep, + bool turn, + bool useRule50, + struct TbRootMoves *results) { + Pos pos = + { + white, + black, + kings, + queens, + rooks, + bishops, + knights, + pawns, + (uint8_t)rule50, + (uint8_t)ep, + turn + }; + if (castling != 0) return 0; + return root_probe_wdl(&pos, useRule50, results); +} + +// Given a position, produce a text string of the form KQPvKRP, where +// "KQP" represents the white pieces if flip == false and the black pieces +// if flip == true. +static void prt_str(const Pos *pos, char *str, bool flip) +{ + int color = flip ? BLACK : WHITE; + + for (int pt = KING; pt >= PAWN; pt--) + for (int i = popcount(pieces_by_type(pos, (Color)color, (PieceType)pt)); i > 0; i--) + *str++ = piece_to_char[pt]; + *str++ = 'v'; + color ^= 1; + for (int pt = KING; pt >= PAWN; pt--) + for (int i = popcount(pieces_by_type(pos, (Color)color, (PieceType)pt)); i > 0; i--) + *str++ = piece_to_char[pt]; + *str++ = 0; +} + +static bool test_tb(const char *str, const char *suffix) +{ + FD fd = open_tb(str, suffix); + if (fd != FD_ERR) { + size_t size = file_size(fd); + close_tb(fd); + if ((size & 63) != 16) { + fprintf(stderr, "Incomplete tablebase file %s.%s\n", str, suffix); + printf("info string Incomplete tablebase file %s.%s\n", str, suffix); + fd = FD_ERR; + } + } + return fd != FD_ERR; +} + +static void *map_tb(const char *name, const char *suffix, map_t *mapping) +{ + FD fd = open_tb(name, suffix); + if (fd == FD_ERR) + return NULL; + + void *data = map_file(fd, mapping); + if (data == NULL) { + fprintf(stderr, "Could not map %s%s into memory.\n", name, suffix); + exit(EXIT_FAILURE); + } + + close_tb(fd); + + return data; +} + +static void add_to_hash(struct BaseEntry *ptr, uint64_t key) +{ + int idx; + + idx = key >> (64 - TB_HASHBITS); + while (tbHash[idx].ptr) + idx = (idx + 1) & ((1 << TB_HASHBITS) - 1); + + tbHash[idx].key = key; + tbHash[idx].ptr = ptr; + atomic_init(&tbHash[idx].error, false); +} + +#define pchr(i) piece_to_char[QUEEN - (i)] +#define Swap(a,b) {int tmp=a;a=b;b=tmp;} + +static void init_tb(char *str) +{ + if (!test_tb(str, tbSuffix[WDL])) + return; + + int pcs[16]; + for (int i = 0; i < 16; i++) + pcs[i] = 0; + int color = 0; + for (char *s = str; *s; s++) + if (*s == 'v') + color = 8; + else { + int piece_type = char_to_piece_type(*s); + if (piece_type) { + assert((piece_type | color) < 16); + pcs[piece_type | color]++; + } + } + + uint64_t key = calc_key_from_pcs(pcs, false); + uint64_t key2 = calc_key_from_pcs(pcs, true); + + bool hasPawns = pcs[W_PAWN] || pcs[B_PAWN]; + + struct BaseEntry *be = hasPawns ? &pawnEntry[tbNumPawn++].be + : &pieceEntry[tbNumPiece++].be; + be->hasPawns = hasPawns; + be->key = key; + be->symmetric = key == key2; + be->num = 0; + for (int i = 0; i < 16; i++) + be->num += pcs[i]; + + numWdl++; + numDtm += be->hasDtm = test_tb(str, tbSuffix[DTM]); + numDtz += be->hasDtz = test_tb(str, tbSuffix[DTZ]); + + if (be->num > TB_MaxCardinality) { + TB_MaxCardinality = be->num; + } + if (be->hasDtm) + if (be->num > TB_MaxCardinalityDTM) { + TB_MaxCardinalityDTM = be->num; + } + +#if !defined(__cplusplus) || (__cplusplus < 202002L) + for (int type = 0; type < 3; type++) + atomic_init(&be->ready[type], false); +#endif + + if (!be->hasPawns) { + int j = 0; + for (int i = 0; i < 16; i++) + if (pcs[i] == 1) j++; + be->kk_enc = j == 2; + } else { + be->pawns[0] = pcs[W_PAWN]; + be->pawns[1] = pcs[B_PAWN]; + if (pcs[B_PAWN] && (!pcs[W_PAWN] || pcs[W_PAWN] > pcs[B_PAWN])) + Swap(be->pawns[0], be->pawns[1]); + } + + add_to_hash(be, key); + if (key != key2) + add_to_hash(be, key2); +} + +#define PIECE(x) ((struct PieceEntry *)(x)) +#define PAWN(x) ((struct PawnEntry *)(x)) + +int num_tables(struct BaseEntry *be, const int type) +{ + return be->hasPawns ? type == DTM ? 6 : 4 : 1; +} + +struct EncInfo *first_ei(struct BaseEntry *be, const int type) +{ + return be->hasPawns + ? &PAWN(be)->ei[type == WDL ? 0 : type == DTM ? 8 : 20] + : &PIECE(be)->ei[type == WDL ? 0 : type == DTM ? 2 : 4]; +} + +static void free_tb_entry(struct BaseEntry *be) +{ + for (int type = 0; type < 3; type++) { + if (atomic_load_explicit(&be->ready[type], memory_order_relaxed)) { + unmap_file((void*)(be->data[type]), be->mapping[type]); + int num = num_tables(be, type); + struct EncInfo *ei = first_ei(be, type); + for (int t = 0; t < num; t++) { + free(ei[t].precomp); + if (type != DTZ) + free(ei[num + t].precomp); + } + atomic_store_explicit(&be->ready[type], false, memory_order_relaxed); + } + } +} + +bool tb_init(const char *path) +{ + if (!initialized) { + init_indices(); + king_attacks_init(); + knight_attacks_init(); + bishop_attacks_init(); + rook_attacks_init(); + pawn_attacks_init(); + initialized = 1; + } + + // if pathString is set, we need to clean up first. + if (pathString) { + free(pathString); + free(paths); + + for (int i = 0; i < tbNumPiece; i++) + free_tb_entry((struct BaseEntry *)&pieceEntry[i]); + for (int i = 0; i < tbNumPawn; i++) + free_tb_entry((struct BaseEntry *)&pawnEntry[i]); + + LOCK_DESTROY(tbMutex); + + pathString = NULL; + numWdl = numDtm = numDtz = 0; + } + + TB_LARGEST = 0; + + // if path is an empty string or equals "", we are done. + const char *p = path; + if (strlen(p) == 0 || !strcmp(p, "")) { + return true; + } + + pathString = (char*)malloc(strlen(p) + 1); + strcpy(pathString, p); + numPaths = 0; + for (int i = 0;; i++) { + if (pathString[i] != SEP_CHAR) + numPaths++; + while (pathString[i] && pathString[i] != SEP_CHAR) + i++; + if (!pathString[i]) break; + pathString[i] = 0; + } + paths = (char**)malloc(numPaths * sizeof(*paths)); + for (int i = 0, j = 0; i < numPaths; i++) { + while (!pathString[j]) j++; + paths[i] = &pathString[j]; + while (pathString[j]) j++; + } + + LOCK_INIT(tbMutex); + + tbNumPiece = tbNumPawn = 0; + TB_MaxCardinality = TB_MaxCardinalityDTM = 0; + + if (!pieceEntry) { +#if defined(__cplusplus) && (__cplusplus >= 202002L) + /* Fix crash with -std=c++20 by being consistent with BaseEntry initialization: + + #if __cplusplus >= 202002L + atomic ready[3]{false, false, false}; + #else + ... + + C++ initialization does not work with malloc. + Also: with new, if the allocation fails we get std::bad_alloc exception, + which is better library behavior than just calling exit. + */ + + pieceEntry = new PieceEntry[TB_MAX_PIECE](); + pawnEntry = new PawnEntry[TB_MAX_PAWN](); +#else + pieceEntry = (struct PieceEntry*)malloc(TB_MAX_PIECE * sizeof(*pieceEntry)); + pawnEntry = (struct PawnEntry*)malloc(TB_MAX_PAWN * sizeof(*pawnEntry)); +#endif + if (!pieceEntry || !pawnEntry) { + fprintf(stderr, "Out of memory.\n"); + exit(EXIT_FAILURE); + } + } + + for (int i = 0; i < (1 << TB_HASHBITS); i++) { + tbHash[i].key = 0; + tbHash[i].ptr = NULL; + } + + char str[16]; + int i, j, k, l, m; + + for (i = 0; i < 5; i++) { + snprintf(str, 16, "K%cvK", pchr(i)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) { + snprintf(str, 16, "K%cvK%c", pchr(i), pchr(j)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) { + snprintf(str, 16, "K%c%cvK", pchr(i), pchr(j)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = 0; k < 5; k++) { + snprintf(str, 16, "K%c%cvK%c", pchr(i), pchr(j), pchr(k)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = j; k < 5; k++) { + snprintf(str, 16, "K%c%c%cvK", pchr(i), pchr(j), pchr(k)); + init_tb(str); + } + + // 6- and 7-piece TBs make sense only with a 64-bit address space + if (sizeof(size_t) < 8 || TB_PIECES < 6) + goto finished; + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = i; k < 5; k++) + for (l = (i == k) ? j : k; l < 5; l++) { + snprintf(str, 16, "K%c%cvK%c%c", pchr(i), pchr(j), pchr(k), pchr(l)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = j; k < 5; k++) + for (l = 0; l < 5; l++) { + snprintf(str, 16, "K%c%c%cvK%c", pchr(i), pchr(j), pchr(k), pchr(l)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = j; k < 5; k++) + for (l = k; l < 5; l++) { + snprintf(str, 16, "K%c%c%c%cvK", pchr(i), pchr(j), pchr(k), pchr(l)); + init_tb(str); + } + + if (TB_PIECES < 7) + goto finished; + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = j; k < 5; k++) + for (l = k; l < 5; l++) + for (m = l; m < 5; m++) { + snprintf(str, 16, "K%c%c%c%c%cvK", pchr(i), pchr(j), pchr(k), pchr(l), pchr(m)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = j; k < 5; k++) + for (l = k; l < 5; l++) + for (m = 0; m < 5; m++) { + snprintf(str, 16, "K%c%c%c%cvK%c", pchr(i), pchr(j), pchr(k), pchr(l), pchr(m)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = j; k < 5; k++) + for (l = 0; l < 5; l++) + for (m = l; m < 5; m++) { + snprintf(str, 16, "K%c%c%cvK%c%c", pchr(i), pchr(j), pchr(k), pchr(l), pchr(m)); + init_tb(str); + } + +finished: + /* TBD - assumes UCI + printf("info string Found %d WDL, %d DTM and %d DTZ tablebase files.\n", + numWdl, numDtm, numDtz); + fflush(stdout); + */ + // Set TB_LARGEST, for backward compatibility with pre-7-man Fathom + TB_LARGEST = (unsigned)TB_MaxCardinality; + if ((unsigned)TB_MaxCardinalityDTM > TB_LARGEST) { + TB_LARGEST = TB_MaxCardinalityDTM; + } + return true; +} + +void tb_free(void) +{ + tb_init(""); +#if defined __cplusplus && __cplusplus >= 202002L + delete[] pieceEntry; + delete[] pawnEntry; +#else + free(pieceEntry); + free(pawnEntry); +#endif + pieceEntry = NULL; + pawnEntry = NULL; +} + +static const int8_t OffDiag[] = { + 0,-1,-1,-1,-1,-1,-1,-1, + 1, 0,-1,-1,-1,-1,-1,-1, + 1, 1, 0,-1,-1,-1,-1,-1, + 1, 1, 1, 0,-1,-1,-1,-1, + 1, 1, 1, 1, 0,-1,-1,-1, + 1, 1, 1, 1, 1, 0,-1,-1, + 1, 1, 1, 1, 1, 1, 0,-1, + 1, 1, 1, 1, 1, 1, 1, 0 +}; + +static const uint8_t Triangle[] = { + 6, 0, 1, 2, 2, 1, 0, 6, + 0, 7, 3, 4, 4, 3, 7, 0, + 1, 3, 8, 5, 5, 8, 3, 1, + 2, 4, 5, 9, 9, 5, 4, 2, + 2, 4, 5, 9, 9, 5, 4, 2, + 1, 3, 8, 5, 5, 8, 3, 1, + 0, 7, 3, 4, 4, 3, 7, 0, + 6, 0, 1, 2, 2, 1, 0, 6 +}; + +static const uint8_t FlipDiag[] = { + 0, 8, 16, 24, 32, 40, 48, 56, + 1, 9, 17, 25, 33, 41, 49, 57, + 2, 10, 18, 26, 34, 42, 50, 58, + 3, 11, 19, 27, 35, 43, 51, 59, + 4, 12, 20, 28, 36, 44, 52, 60, + 5, 13, 21, 29, 37, 45, 53, 61, + 6, 14, 22, 30, 38, 46, 54, 62, + 7, 15, 23, 31, 39, 47, 55, 63 +}; + +static const uint8_t Lower[] = { + 28, 0, 1, 2, 3, 4, 5, 6, + 0, 29, 7, 8, 9, 10, 11, 12, + 1, 7, 30, 13, 14, 15, 16, 17, + 2, 8, 13, 31, 18, 19, 20, 21, + 3, 9, 14, 18, 32, 22, 23, 24, + 4, 10, 15, 19, 22, 33, 25, 26, + 5, 11, 16, 20, 23, 25, 34, 27, + 6, 12, 17, 21, 24, 26, 27, 35 +}; + +static const uint8_t Diag[] = { + 0, 0, 0, 0, 0, 0, 0, 8, + 0, 1, 0, 0, 0, 0, 9, 0, + 0, 0, 2, 0, 0, 10, 0, 0, + 0, 0, 0, 3, 11, 0, 0, 0, + 0, 0, 0, 12, 4, 0, 0, 0, + 0, 0, 13, 0, 0, 5, 0, 0, + 0, 14, 0, 0, 0, 0, 6, 0, + 15, 0, 0, 0, 0, 0, 0, 7 +}; + +static const uint8_t Flap[2][64] = { + { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6, 12, 18, 18, 12, 6, 0, + 1, 7, 13, 19, 19, 13, 7, 1, + 2, 8, 14, 20, 20, 14, 8, 2, + 3, 9, 15, 21, 21, 15, 9, 3, + 4, 10, 16, 22, 22, 16, 10, 4, + 5, 11, 17, 23, 23, 17, 11, 5, + 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 3, 2, 1, 0, + 4, 5, 6, 7, 7, 6, 5, 4, + 8, 9, 10, 11, 11, 10, 9, 8, + 12, 13, 14, 15, 15, 14, 13, 12, + 16, 17, 18, 19, 19, 18, 17, 16, + 20, 21, 22, 23, 23, 22, 21, 20, + 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +static const uint8_t PawnTwist[2][64] = { + { 0, 0, 0, 0, 0, 0, 0, 0, + 47, 35, 23, 11, 10, 22, 34, 46, + 45, 33, 21, 9, 8, 20, 32, 44, + 43, 31, 19, 7, 6, 18, 30, 42, + 41, 29, 17, 5, 4, 16, 28, 40, + 39, 27, 15, 3, 2, 14, 26, 38, + 37, 25, 13, 1, 0, 12, 24, 36, + 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, + 47, 45, 43, 41, 40, 42, 44, 46, + 39, 37, 35, 33, 32, 34, 36, 38, + 31, 29, 27, 25, 24, 26, 28, 30, + 23, 21, 19, 17, 16, 18, 20, 22, + 15, 13, 11, 9, 8, 10, 12, 14, + 7, 5, 3, 1, 0, 2, 4, 6, + 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +static const int16_t KKIdx[10][64] = { + { -1, -1, -1, 0, 1, 2, 3, 4, + -1, -1, -1, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57 }, + { 58, -1, -1, -1, 59, 60, 61, 62, + 63, -1, -1, -1, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, + 100,101,102,103,104,105,106,107, + 108,109,110,111,112,113,114,115}, + {116,117, -1, -1, -1,118,119,120, + 121,122, -1, -1, -1,123,124,125, + 126,127,128,129,130,131,132,133, + 134,135,136,137,138,139,140,141, + 142,143,144,145,146,147,148,149, + 150,151,152,153,154,155,156,157, + 158,159,160,161,162,163,164,165, + 166,167,168,169,170,171,172,173 }, + {174, -1, -1, -1,175,176,177,178, + 179, -1, -1, -1,180,181,182,183, + 184, -1, -1, -1,185,186,187,188, + 189,190,191,192,193,194,195,196, + 197,198,199,200,201,202,203,204, + 205,206,207,208,209,210,211,212, + 213,214,215,216,217,218,219,220, + 221,222,223,224,225,226,227,228 }, + {229,230, -1, -1, -1,231,232,233, + 234,235, -1, -1, -1,236,237,238, + 239,240, -1, -1, -1,241,242,243, + 244,245,246,247,248,249,250,251, + 252,253,254,255,256,257,258,259, + 260,261,262,263,264,265,266,267, + 268,269,270,271,272,273,274,275, + 276,277,278,279,280,281,282,283 }, + {284,285,286,287,288,289,290,291, + 292,293, -1, -1, -1,294,295,296, + 297,298, -1, -1, -1,299,300,301, + 302,303, -1, -1, -1,304,305,306, + 307,308,309,310,311,312,313,314, + 315,316,317,318,319,320,321,322, + 323,324,325,326,327,328,329,330, + 331,332,333,334,335,336,337,338 }, + { -1, -1,339,340,341,342,343,344, + -1, -1,345,346,347,348,349,350, + -1, -1,441,351,352,353,354,355, + -1, -1, -1,442,356,357,358,359, + -1, -1, -1, -1,443,360,361,362, + -1, -1, -1, -1, -1,444,363,364, + -1, -1, -1, -1, -1, -1,445,365, + -1, -1, -1, -1, -1, -1, -1,446 }, + { -1, -1, -1,366,367,368,369,370, + -1, -1, -1,371,372,373,374,375, + -1, -1, -1,376,377,378,379,380, + -1, -1, -1,447,381,382,383,384, + -1, -1, -1, -1,448,385,386,387, + -1, -1, -1, -1, -1,449,388,389, + -1, -1, -1, -1, -1, -1,450,390, + -1, -1, -1, -1, -1, -1, -1,451 }, + {452,391,392,393,394,395,396,397, + -1, -1, -1, -1,398,399,400,401, + -1, -1, -1, -1,402,403,404,405, + -1, -1, -1, -1,406,407,408,409, + -1, -1, -1, -1,453,410,411,412, + -1, -1, -1, -1, -1,454,413,414, + -1, -1, -1, -1, -1, -1,455,415, + -1, -1, -1, -1, -1, -1, -1,456 }, + {457,416,417,418,419,420,421,422, + -1,458,423,424,425,426,427,428, + -1, -1, -1, -1, -1,429,430,431, + -1, -1, -1, -1, -1,432,433,434, + -1, -1, -1, -1, -1,435,436,437, + -1, -1, -1, -1, -1,459,438,439, + -1, -1, -1, -1, -1, -1,460,440, + -1, -1, -1, -1, -1, -1, -1,461 } +}; + +static const uint8_t FileToFile[] = { 0, 1, 2, 3, 3, 2, 1, 0 }; +static const int WdlToMap[5] = { 1, 3, 0, 2, 0 }; +static const uint8_t PAFlags[5] = { 8, 0, 0, 0, 4 }; + +static size_t Binomial[7][64]; +static size_t PawnIdx[2][6][24]; +static size_t PawnFactorFile[6][4]; +static size_t PawnFactorRank[6][6]; + +static void init_indices(void) +{ + int i, j, k; + + // Binomial[k][n] = Bin(n, k) + for (i = 0; i < 7; i++) + for (j = 0; j < 64; j++) { + size_t f = 1; + size_t l = 1; + for (k = 0; k < i; k++) { + f *= (j - k); + l *= (k + 1); + } + Binomial[i][j] = f / l; + } + + for (i = 0; i < 6; i++) { + size_t s = 0; + for (j = 0; j < 24; j++) { + PawnIdx[0][i][j] = s; + s += Binomial[i][PawnTwist[0][(1 + (j % 6)) * 8 + (j / 6)]]; + if ((j + 1) % 6 == 0) { + PawnFactorFile[i][j / 6] = s; + s = 0; + } + } + } + + for (i = 0; i < 6; i++) { + size_t s = 0; + for (j = 0; j < 24; j++) { + PawnIdx[1][i][j] = s; + s += Binomial[i][PawnTwist[1][(1 + (j / 4)) * 8 + (j % 4)]]; + if ((j + 1) % 4 == 0) { + PawnFactorRank[i][j / 4] = s; + s = 0; + } + } + } +} + +int leading_pawn(int *p, struct BaseEntry *be, const int enc) +{ + for (int i = 1; i < be->pawns[0]; i++) + if (Flap[enc-1][p[0]] > Flap[enc-1][p[i]]) + Swap(p[0], p[i]); + + return enc == FILE_ENC ? FileToFile[p[0] & 7] : (p[0] - 8) >> 3; +} + +size_t encode(int *p, struct EncInfo *ei, struct BaseEntry *be, + const int enc) +{ + int n = be->num; + size_t idx; + int k; + + if (p[0] & 0x04) + for (int i = 0; i < n; i++) + p[i] ^= 0x07; + + if (enc == PIECE_ENC) { + if (p[0] & 0x20) + for (int i = 0; i < n; i++) + p[i] ^= 0x38; + + for (int i = 0; i < n; i++) + if (OffDiag[p[i]]) { + if (OffDiag[p[i]] > 0 && i < (be->kk_enc ? 2 : 3)) + for (int j = 0; j < n; j++) + p[j] = FlipDiag[p[j]]; + break; + } + + if (be->kk_enc) { + idx = KKIdx[Triangle[p[0]]][p[1]]; + k = 2; + } else { + int s1 = (p[1] > p[0]); + int s2 = (p[2] > p[0]) + (p[2] > p[1]); + + if (OffDiag[p[0]]) + idx = Triangle[p[0]] * 63*62 + (p[1] - s1) * 62 + (p[2] - s2); + else if (OffDiag[p[1]]) + idx = 6*63*62 + Diag[p[0]] * 28*62 + Lower[p[1]] * 62 + p[2] - s2; + else if (OffDiag[p[2]]) + idx = 6*63*62 + 4*28*62 + Diag[p[0]] * 7*28 + (Diag[p[1]] - s1) * 28 + Lower[p[2]]; + else + idx = 6*63*62 + 4*28*62 + 4*7*28 + Diag[p[0]] * 7*6 + (Diag[p[1]] - s1) * 6 + (Diag[p[2]] - s2); + k = 3; + } + idx *= ei->factor[0]; + } else { + for (int i = 1; i < be->pawns[0]; i++) + for (int j = i + 1; j < be->pawns[0]; j++) + if (PawnTwist[enc-1][p[i]] < PawnTwist[enc-1][p[j]]) + Swap(p[i], p[j]); + + k = be->pawns[0]; + idx = PawnIdx[enc-1][k-1][Flap[enc-1][p[0]]]; + for (int i = 1; i < k; i++) + idx += Binomial[k-i][PawnTwist[enc-1][p[i]]]; + idx *= ei->factor[0]; + + // Pawns of other color + if (be->pawns[1]) { + int t = k + be->pawns[1]; + for (int i = k; i < t; i++) + for (int j = i + 1; j < t; j++) + if (p[i] > p[j]) Swap(p[i], p[j]); + size_t s = 0; + for (int i = k; i < t; i++) { + int sq = p[i]; + int skips = 0; + for (int j = 0; j < k; j++) + skips += (sq > p[j]); + s += Binomial[i - k + 1][sq - skips - 8]; + } + idx += s * ei->factor[k]; + k = t; + } + } + + for (; k < n;) { + int t = k + ei->norm[k]; + for (int i = k; i < t; i++) + for (int j = i + 1; j < t; j++) + if (p[i] > p[j]) Swap(p[i], p[j]); + size_t s = 0; + for (int i = k; i < t; i++) { + int sq = p[i]; + int skips = 0; + for (int j = 0; j < k; j++) + skips += (sq > p[j]); + s += Binomial[i - k + 1][sq - skips]; + } + idx += s * ei->factor[k]; + k = t; + } + + return idx; +} + +static size_t encode_piece(int *p, struct EncInfo *ei, struct BaseEntry *be) +{ + return encode(p, ei, be, PIECE_ENC); +} + +static size_t encode_pawn_f(int *p, struct EncInfo *ei, struct BaseEntry *be) +{ + return encode(p, ei, be, FILE_ENC); +} + +static size_t encode_pawn_r(int *p, struct EncInfo *ei, struct BaseEntry *be) +{ + return encode(p, ei, be, RANK_ENC); +} + +// Count number of placements of k like pieces on n squares +static size_t subfactor(size_t k, size_t n) +{ + size_t f = n; + size_t l = 1; + for (size_t i = 1; i < k; i++) { + f *= n - i; + l *= i + 1; + } + + return f / l; +} + +static size_t init_enc_info(struct EncInfo *ei, struct BaseEntry *be, + uint8_t *tb, int shift, int t, const int enc) +{ + bool morePawns = enc != PIECE_ENC && be->pawns[1] > 0; + + for (int i = 0; i < be->num; i++) { + ei->pieces[i] = (tb[i + 1 + morePawns] >> shift) & 0x0f; + ei->norm[i] = 0; + } + + int order = (tb[0] >> shift) & 0x0f; + int order2 = morePawns ? (tb[1] >> shift) & 0x0f : 0x0f; + + int k = ei->norm[0] = enc != PIECE_ENC ? be->pawns[0] + : be->kk_enc ? 2 : 3; + + if (morePawns) { + ei->norm[k] = be->pawns[1]; + k += ei->norm[k]; + } + + for (int i = k; i < be->num; i += ei->norm[i]) + for (int j = i; j < be->num && ei->pieces[j] == ei->pieces[i]; j++) + ei->norm[i]++; + + int n = 64 - k; + size_t f = 1; + + for (int i = 0; k < be->num || i == order || i == order2; i++) { + if (i == order) { + ei->factor[0] = f; + f *= enc == FILE_ENC ? PawnFactorFile[ei->norm[0] - 1][t] + : enc == RANK_ENC ? PawnFactorRank[ei->norm[0] - 1][t] + : be->kk_enc ? 462 : 31332; + } else if (i == order2) { + ei->factor[ei->norm[0]] = f; + f *= subfactor(ei->norm[ei->norm[0]], 48 - ei->norm[0]); + } else { + ei->factor[k] = f; + f *= subfactor(ei->norm[k], n); + n -= ei->norm[k]; + k += ei->norm[k]; + } + } + + return f; +} + +static void calc_symLen(struct PairsData *d, uint32_t s, char *tmp) +{ + uint8_t *w = d->symPat + 3 * s; + uint32_t s2 = (w[2] << 4) | (w[1] >> 4); + if (s2 == 0x0fff) + d->symLen[s] = 0; + else { + uint32_t s1 = ((w[1] & 0xf) << 8) | w[0]; + if (!tmp[s1]) calc_symLen(d, s1, tmp); + if (!tmp[s2]) calc_symLen(d, s2, tmp); + d->symLen[s] = d->symLen[s1] + d->symLen[s2] + 1; + } + tmp[s] = 1; +} + +static struct PairsData *setup_pairs(uint8_t **ptr, size_t tb_size, + size_t *size, uint8_t *flags, int type) +{ + struct PairsData *d; + uint8_t *data = *ptr; + + *flags = data[0]; + if (data[0] & 0x80) { + d = (struct PairsData*)malloc(sizeof(struct PairsData)); + d->idxBits = 0; + d->constValue[0] = type == WDL ? data[1] : 0; + d->constValue[1] = 0; + *ptr = data + 2; + size[0] = size[1] = size[2] = 0; + return d; + } + + uint8_t blockSize = data[1]; + uint8_t idxBits = data[2]; + uint32_t realNumBlocks = read_le_u32(data+4); + uint32_t numBlocks = realNumBlocks + data[3]; + int maxLen = data[8]; + int minLen = data[9]; + int h = maxLen - minLen + 1; + uint32_t numSyms = (uint32_t)read_le_u16(data + 10 + 2 * h); + d = (struct PairsData*)malloc(sizeof(struct PairsData) + h * sizeof(uint64_t) + numSyms); + d->blockSize = blockSize; + d->idxBits = idxBits; + d->offset = (uint16_t *)(&data[10]); + d->symLen = (uint8_t *)d + sizeof(struct PairsData) + h * sizeof(uint64_t); + d->symPat = &data[12 + 2 * h]; + d->minLen = minLen; + *ptr = &data[12 + 2 * h + 3 * numSyms + (numSyms & 1)]; + + size_t num_indices = (tb_size + (1ULL << idxBits) - 1) >> idxBits; + size[0] = 6ULL * num_indices; + size[1] = 2ULL * numBlocks; + size[2] = (size_t)realNumBlocks << blockSize; + + assert(numSyms < TB_MAX_SYMS); + char tmp[TB_MAX_SYMS]; + memset(tmp, 0, numSyms); + for (uint32_t s = 0; s < numSyms; s++) + if (!tmp[s]) + calc_symLen(d, s, tmp); + + d->base[h - 1] = 0; + for (int i = h - 2; i >= 0; i--) + d->base[i] = (d->base[i + 1] + read_le_u16((uint8_t *)(d->offset + i)) - read_le_u16((uint8_t *)(d->offset + i + 1))) / 2; +#ifdef DECOMP64 + for (int i = 0; i < h; i++) + d->base[i] <<= 64 - (minLen + i); +#else + for (int i = 0; i < h; i++) + d->base[i] <<= 32 - (minLen + i); +#endif + d->offset -= d->minLen; + + return d; +} + +static bool init_table(struct BaseEntry *be, const char *str, int type) +{ + uint8_t *data = (uint8_t*)map_tb(str, tbSuffix[type], &be->mapping[type]); + if (!data) return false; + + if (read_le_u32(data) != tbMagic[type]) { + fprintf(stderr, "Corrupted table.\n"); + unmap_file((void*)data, be->mapping[type]); + return false; + } + + be->data[type] = data; + + bool split = type != DTZ && (data[4] & 0x01); + if (type == DTM) + be->dtmLossOnly = data[4] & 0x04; + + data += 5; + + size_t tb_size[6][2]; + int num = num_tables(be, type); + struct EncInfo *ei = first_ei(be, type); + int enc = !be->hasPawns ? PIECE_ENC : type != DTM ? FILE_ENC : RANK_ENC; + + for (int t = 0; t < num; t++) { + tb_size[t][0] = init_enc_info(&ei[t], be, data, 0, t, enc); + if (split) + tb_size[t][1] = init_enc_info(&ei[num + t], be, data, 4, t, enc); + data += be->num + 1 + (be->hasPawns && be->pawns[1]); + } + data += (uintptr_t)data & 1; + + size_t size[6][2][3]; + for (int t = 0; t < num; t++) { + uint8_t flags; + ei[t].precomp = setup_pairs(&data, tb_size[t][0], size[t][0], &flags, type); + if (type == DTZ) { + if (!be->hasPawns) + PIECE(be)->dtzFlags = flags; + else + PAWN(be)->dtzFlags[t] = flags; + } + if (split) + ei[num + t].precomp = setup_pairs(&data, tb_size[t][1], size[t][1], &flags, type); + else if (type != DTZ) + ei[num + t].precomp = NULL; + } + + if (type == DTM && !be->dtmLossOnly) { + uint16_t *map = (uint16_t *)data; + *(be->hasPawns ? &PAWN(be)->dtmMap : &PIECE(be)->dtmMap) = map; + uint16_t (*mapIdx)[2][2] = be->hasPawns ? &PAWN(be)->dtmMapIdx[0] + : &PIECE(be)->dtmMapIdx; + for (int t = 0; t < num; t++) { + for (int i = 0; i < 2; i++) { + mapIdx[t][0][i] = (uint16_t)(data + 1 - (uint8_t*)map); + data += 2 + 2 * read_le_u16(data); + } + if (split) { + for (int i = 0; i < 2; i++) { + mapIdx[t][1][i] = (uint16_t)(data + 1 - (uint8_t*)map); + data += 2 + 2 * read_le_u16(data); + } + } + } + } + + if (type == DTZ) { + void *map = data; + *(be->hasPawns ? &PAWN(be)->dtzMap : &PIECE(be)->dtzMap) = map; + uint16_t (*mapIdx)[4] = be->hasPawns ? &PAWN(be)->dtzMapIdx[0] + : &PIECE(be)->dtzMapIdx; + uint8_t *flags = be->hasPawns ? &PAWN(be)->dtzFlags[0] + : &PIECE(be)->dtzFlags; + for (int t = 0; t < num; t++) { + if (flags[t] & 2) { + if (!(flags[t] & 16)) { + for (int i = 0; i < 4; i++) { + mapIdx[t][i] = (uint16_t)(data + 1 - (uint8_t *)map); + data += 1 + data[0]; + } + } else { + data += (uintptr_t)data & 0x01; + for (int i = 0; i < 4; i++) { + mapIdx[t][i] = (uint16_t)((uint16_t*)data + 1 - (uint16_t *)map); + data += 2 + 2 * read_le_u16(data); + } + } + } + } + data += (uintptr_t)data & 0x01; + } + + for (int t = 0; t < num; t++) { + ei[t].precomp->indexTable = data; + data += size[t][0][0]; + if (split) { + ei[num + t].precomp->indexTable = data; + data += size[t][1][0]; + } + } + + for (int t = 0; t < num; t++) { + ei[t].precomp->sizeTable = (uint16_t *)data; + data += size[t][0][1]; + if (split) { + ei[num + t].precomp->sizeTable = (uint16_t *)data; + data += size[t][1][1]; + } + } + + for (int t = 0; t < num; t++) { + data = (uint8_t *)(((uintptr_t)data + 0x3f) & ~0x3f); + ei[t].precomp->data = data; + data += size[t][0][2]; + if (split) { + data = (uint8_t *)(((uintptr_t)data + 0x3f) & ~0x3f); + ei[num + t].precomp->data = data; + data += size[t][1][2]; + } + } + + if (type == DTM && be->hasPawns) + PAWN(be)->dtmSwitched = + calc_key_from_pieces(ei[0].pieces, be->num) != be->key; + + return true; +} + +static uint8_t *decompress_pairs(struct PairsData *d, size_t idx) +{ + if (!d->idxBits) + return d->constValue; + + uint32_t mainIdx = (uint32_t)(idx >> d->idxBits); + int litIdx = (idx & (((size_t)1 << d->idxBits) - 1)) - ((size_t)1 << (d->idxBits - 1)); + uint32_t block; + memcpy(&block, d->indexTable + 6 * mainIdx, sizeof(block)); + block = from_le_u32(block); + + uint16_t idxOffset = *(uint16_t *)(d->indexTable + 6 * mainIdx + 4); + litIdx += from_le_u16(idxOffset); + + if (litIdx < 0) + while (litIdx < 0) + litIdx += d->sizeTable[--block] + 1; + else + while (litIdx > d->sizeTable[block]) + litIdx -= d->sizeTable[block++] + 1; + + uint32_t *ptr = (uint32_t *)(d->data + ((size_t)block << d->blockSize)); + + int m = d->minLen; + uint16_t *offset = d->offset; + uint64_t *base = d->base - m; + uint8_t *symLen = d->symLen; + uint32_t sym, bitCnt; + +#ifdef DECOMP64 + uint64_t code = from_be_u64(*(uint64_t *)ptr); + + ptr += 2; + bitCnt = 0; // number of "empty bits" in code + for (;;) { + int l = m; + while (code < base[l]) l++; + sym = from_le_u16(offset[l]); + sym += (uint32_t)((code - base[l]) >> (64 - l)); + if (litIdx < (int)symLen[sym] + 1) break; + litIdx -= (int)symLen[sym] + 1; + code <<= l; + bitCnt += l; + if (bitCnt >= 32) { + bitCnt -= 32; + uint32_t tmp = from_be_u32(*ptr++); + code |= (uint64_t)tmp << bitCnt; + } + } +#else + uint32_t next = 0; + uint32_t data = *ptr++; + uint32_t code = from_be_u32(data); + bitCnt = 0; // number of bits in next + for (;;) { + int l = m; + while (code < base[l]) l++; + sym = offset[l] + ((code - base[l]) >> (32 - l)); + if (litIdx < (int)symLen[sym] + 1) break; + litIdx -= (int)symLen[sym] + 1; + code <<= l; + if (bitCnt < l) { + if (bitCnt) { + code |= (next >> (32 - l)); + l -= bitCnt; + } + data = *ptr++; + next = from_be_u32(data); + bitCnt = 32; + } + code |= (next >> (32 - l)); + next <<= l; + bitCnt -= l; + } +#endif + uint8_t *symPat = d->symPat; + while (symLen[sym] != 0) { + uint8_t *w = symPat + (3 * sym); + int s1 = ((w[1] & 0xf) << 8) | w[0]; + if (litIdx < (int)symLen[s1] + 1) + sym = s1; + else { + litIdx -= (int)symLen[s1] + 1; + sym = (w[2] << 4) | (w[1] >> 4); + } + } + + return &symPat[3 * sym]; +} + +// p[i] is to contain the square 0-63 (A1-H8) for a piece of type +// pc[i] ^ flip, where 1 = white pawn, ..., 14 = black king and pc ^ flip +// flips between white and black if flip == true. +// Pieces of the same type are guaranteed to be consecutive. +inline static int fill_squares(const Pos *pos, uint8_t *pc, bool flip, int mirror, int *p, + int i) +{ + Color color = ColorOfPiece(pc[i]); + if (flip) color = (Color)(!(int)color); + uint64_t bb = pieces_by_type(pos, color, TypeOfPiece(pc[i])); + unsigned sq; + do { + sq = lsb(bb); + p[i++] = sq ^ mirror; + bb = poplsb(bb); + } while (bb); + return i; +} + +static int probe_table(const Pos *pos, int s, int *success, const int type) +{ + // Obtain the position's material-signature key + uint64_t key = calc_key(pos,false); + + // Test for KvK + // Note: Cfish has key == 2ULL for KvK but we have 0 + if (type == WDL && key == 0ULL) + return 0; + + int hashIdx = key >> (64 - TB_HASHBITS); + while (tbHash[hashIdx].key && tbHash[hashIdx].key != key) + hashIdx = (hashIdx + 1) & ((1 << TB_HASHBITS) - 1); + if (!tbHash[hashIdx].ptr || atomic_load_explicit(&tbHash[hashIdx].error, memory_order_relaxed)) { + *success = 0; + return 0; + } + + struct BaseEntry *be = tbHash[hashIdx].ptr; + if ((type == DTM && !be->hasDtm) || (type == DTZ && !be->hasDtz)) { + *success = 0; + return 0; + } + + // Use double-checked locking to reduce locking overhead + if (!atomic_load_explicit(&be->ready[type], memory_order_acquire)) { + LOCK(tbMutex); + if (atomic_load_explicit(&tbHash[hashIdx].error, memory_order_relaxed)) { + *success = 0; + UNLOCK(tbMutex); + return 0; + } + if (!atomic_load_explicit(&be->ready[type], memory_order_relaxed)) { + char str[16]; + prt_str(pos, str, be->key != key); + if (!init_table(be, str, type)) { + atomic_store_explicit(&tbHash[hashIdx].error, true, memory_order_relaxed); + *success = 0; + UNLOCK(tbMutex); + return 0; + } + atomic_store_explicit(&be->ready[type], true, memory_order_release); + } + UNLOCK(tbMutex); + } + + bool bside, flip; + if (!be->symmetric) { + flip = key != be->key; + bside = (pos->turn == WHITE) == flip; + if (type == DTM && be->hasPawns && PAWN(be)->dtmSwitched) { + flip = !flip; + bside = !bside; + } + } else { + flip = pos->turn != WHITE; + bside = false; + } + + struct EncInfo *ei = first_ei(be, type); + int p[TB_PIECES]; + size_t idx; + int t = 0; + uint8_t flags = 0; // initialize to fix GCC warning + + if (!be->hasPawns) { + if (type == DTZ) { + flags = PIECE(be)->dtzFlags; + if ((flags & 1) != bside && !be->symmetric) { + *success = -1; + return 0; + } + } + ei = type != DTZ ? &ei[bside] : ei; + for (int i = 0; i < be->num;) + i = fill_squares(pos, ei->pieces, flip, 0, p, i); + idx = encode_piece(p, ei, be); + } else { + int i = fill_squares(pos, ei->pieces, flip, flip ? 0x38 : 0, p, 0); + t = leading_pawn(p, be, type != DTM ? FILE_ENC : RANK_ENC); + if (type == DTZ) { + flags = PAWN(be)->dtzFlags[t]; + if ((flags & 1) != bside && !be->symmetric) { + *success = -1; + return 0; + } + } + ei = type == WDL ? &ei[t + 4 * bside] + : type == DTM ? &ei[t + 6 * bside] : &ei[t]; + while (i < be->num) + i = fill_squares(pos, ei->pieces, flip, flip ? 0x38 : 0, p, i); + idx = type != DTM ? encode_pawn_f(p, ei, be) : encode_pawn_r(p, ei, be); + } + + uint8_t *w = decompress_pairs(ei->precomp, idx); + + if (type == WDL) + return (int)w[0] - 2; + + int v = w[0] + ((w[1] & 0x0f) << 8); + + if (type == DTM) { + if (!be->dtmLossOnly) + v = (int)from_le_u16(be->hasPawns + ? PAWN(be)->dtmMap[PAWN(be)->dtmMapIdx[t][bside][s] + v] + : PIECE(be)->dtmMap[PIECE(be)->dtmMapIdx[bside][s] + v]); + } else { + if (flags & 2) { + int m = WdlToMap[s + 2]; + if (!(flags & 16)) + v = be->hasPawns + ? ((uint8_t *)PAWN(be)->dtzMap)[PAWN(be)->dtzMapIdx[t][m] + v] + : ((uint8_t *)PIECE(be)->dtzMap)[PIECE(be)->dtzMapIdx[m] + v]; + else + v = (int)from_le_u16(be->hasPawns + ? ((uint16_t *)PAWN(be)->dtzMap)[PAWN(be)->dtzMapIdx[t][m] + v] + : ((uint16_t *)PIECE(be)->dtzMap)[PIECE(be)->dtzMapIdx[m] + v]); + } + if (!(flags & PAFlags[s + 2]) || (s & 1)) + v *= 2; + } + + return v; +} + +static int probe_wdl_table(const Pos *pos, int *success) +{ + return probe_table(pos, 0, success, WDL); +} + +static int probe_dtm_table(const Pos *pos, int won, int *success) +{ + return probe_table(pos, won, success, DTM); +} + +static int probe_dtz_table(const Pos *pos, int wdl, int *success) +{ + return probe_table(pos, wdl, success, DTZ); +} + +// probe_ab() is not called for positions with en passant captures. +static int probe_ab(const Pos *pos, int alpha, int beta, int *success) +{ + assert(pos->ep == 0); + + TbMove moves0[TB_MAX_CAPTURES]; + TbMove *m = moves0; + // Generate (at least) all legal captures including (under)promotions. + // It is OK to generate more, as long as they are filtered out below. + TbMove *end = gen_captures(pos, m); + for (; m < end; m++) { + Pos pos1; + TbMove move = *m; + if (!is_capture(pos, move)) + continue; + if (!do_move(&pos1, pos, move)) + continue; // illegal move + int v = -probe_ab(&pos1, -beta, -alpha, success); + if (*success == 0) return 0; + if (v > alpha) { + if (v >= beta) + return v; + alpha = v; + } + } + + int v = probe_wdl_table(pos, success); + + return alpha >= v ? alpha : v; +} + +// Probe the WDL table for a particular position. +// +// If *success != 0, the probe was successful. +// +// If *success == 2, the position has a winning capture, or the position +// is a cursed win and has a cursed winning capture, or the position +// has an ep capture as only best move. +// This is used in probe_dtz(). +// +// The return value is from the point of view of the side to move: +// -2 : loss +// -1 : loss, but draw under 50-move rule +// 0 : draw +// 1 : win, but draw under 50-move rule +// 2 : win +int probe_wdl(Pos *pos, int *success) +{ + *success = 1; + + // Generate (at least) all legal captures including (under)promotions. + TbMove moves0[TB_MAX_CAPTURES]; + TbMove *m = moves0; + TbMove *end = gen_captures(pos, m); + int bestCap = -3, bestEp = -3; + + // We do capture resolution, letting bestCap keep track of the best + // capture without ep rights and letting bestEp keep track of still + // better ep captures if they exist. + + for (; m < end; m++) { + Pos pos1; + TbMove move = *m; + if (!is_capture(pos, move)) + continue; + if (!do_move(&pos1, pos, move)) + continue; // illegal move + int v = -probe_ab(&pos1, -2, -bestCap, success); + if (*success == 0) return 0; + if (v > bestCap) { + if (v == 2) { + *success = 2; + return 2; + } + if (!is_en_passant(pos,move)) + bestCap = v; + else if (v > bestEp) + bestEp = v; + } + } + + int v = probe_wdl_table(pos, success); + if (*success == 0) return 0; + + // Now max(v, bestCap) is the WDL value of the position without ep rights. + // If the position without ep rights is not stalemate or no ep captures + // exist, then the value of the position is max(v, bestCap, bestEp). + // If the position without ep rights is stalemate and bestEp > -3, + // then the value of the position is bestEp (and we will have v == 0). + + if (bestEp > bestCap) { + if (bestEp > v) { // ep capture (possibly cursed losing) is best. + *success = 2; + return bestEp; + } + bestCap = bestEp; + } + + // Now max(v, bestCap) is the WDL value of the position unless + // the position without ep rights is stalemate and bestEp > -3. + + if (bestCap >= v) { + // No need to test for the stalemate case here: either there are + // non-ep captures, or bestCap == bestEp >= v anyway. + *success = 1 + (bestCap > 0); + return bestCap; + } + + // Now handle the stalemate case. + if (bestEp > -3 && v == 0) { + TbMove moves[TB_MAX_MOVES]; + TbMove *end2 = gen_moves(pos, moves); + // Check for stalemate in the position with ep captures. + for (m = moves; m < end2; m++) { + if (!is_en_passant(pos,*m) && legal_move(pos, *m)) break; + } + if (m == end2 && !is_check(pos)) { + // stalemate score from tb (w/o e.p.), but an en-passant capture + // is possible. + *success = 2; + return bestEp; + } + } + // Stalemate / en passant not an issue, so v is the correct value. + + return v; +} + +#if 0 +// This will not be called for positions with en passant captures +static Value probe_dtm_dc(const Pos *pos, int won, int *success) +{ + assert(ep_square() == 0); + + Value v, bestCap = -TB_VALUE_INFINITE; + + TbMove moves0[TB_MAX_CAPTURES]; + TbMove *end, *m = moves0; + + // Generate at least all legal captures including (under)promotions + end = gen_captures(pos, m); + Pos pos1; + for (; m < end; m++) { + TbMove move = m->move; + if (!is_capture(pos, move)) + continue; + if (!do_move(&pos1, pos, move)) + continue; + if (!won) + v = -probe_dtm_dc(&pos1, 1, success) + 1; + else if (probe_ab(&pos1, -1, 0, success) < 0 && *success) + v = -probe_dtm_dc(&pos1, 0, success) - 1; + else + v = -TB_VALUE_INFINITE; + bestCap = max(bestCap,v); + if (*success == 0) return 0; + } + + int dtm = probe_dtm_table(pos, won, success); + v = won ? TB_VALUE_MATE - 2 * dtm + 1 : -TB_VALUE_MATE + 2 * dtm; + + return max(bestCap,v); +} +#endif + +static Value probe_dtm_win(const Pos *pos, int *success); + +// Probe a position known to lose by probing the DTM table and looking +// at captures. +static Value probe_dtm_loss(const Pos *pos, int *success) +{ + Value v, best = -TB_VALUE_INFINITE, numEp = 0; + + TbMove moves0[TB_MAX_CAPTURES]; + // Generate at least all legal captures including (under)promotions + TbMove *end, *m = moves0; + end = gen_captures(pos, m); + + Pos pos1; + for (; m < end; m++) { + TbMove move = *m; + if (!is_capture(pos, move) || !legal_move(pos, move)) + continue; + if (is_en_passant(pos, move)) + numEp++; + do_move(&pos1, pos, move); + v = -probe_dtm_win(&pos1, success) + 1; + if (v > best) { + best = v; + } + if (*success == 0) + return 0; + } + + // If there are en passant captures, the position without ep rights + // may be a stalemate. If it is, we must avoid probing the DTM table. + if (numEp != 0 && gen_legal(pos, m) == m + numEp) + return best; + + v = -TB_VALUE_MATE + 2 * probe_dtm_table(pos, 0, success); + return best > v ? best : v; +} + +static Value probe_dtm_win(const Pos *pos, int *success) +{ + Value v, best = -TB_VALUE_INFINITE; + + // Generate all moves + TbMove moves0[TB_MAX_CAPTURES]; + TbMove *m = moves0; + TbMove *end = gen_moves(pos, m); + // Perform a 1-ply search + Pos pos1; + for (; m < end; m++) { + TbMove move = *m; + if (do_move(&pos1, pos, move)) { + // not legal + continue; + } + if ((pos1.ep > 0 ? probe_wdl(&pos1, success) + : probe_ab(&pos1, -1, 0, success)) < 0 + && *success) + v = -probe_dtm_loss(&pos1, success) - 1; + else + v = -TB_VALUE_INFINITE; + if (v > best) { + best = v; + } + if (*success == 0) return 0; + } + + return best; +} + +Value TB_probe_dtm(const Pos *pos, int wdl, int *success) +{ + assert(wdl != 0); + + *success = 1; + + return wdl > 0 ? probe_dtm_win(pos, success) + : probe_dtm_loss(pos, success); +} + +#if 0 +// To be called only for non-drawn positions. +Value TB_probe_dtm2(const Pos *pos, int wdl, int *success) +{ + assert(wdl != 0); + + *success = 1; + Value v, bestCap = -TB_VALUE_INFINITE, bestEp = -TB_VALUE_INFINITE; + + TbMove moves0[TB_MAX_CAPTURES]; + TbMove *end, *m = moves0; + + // Generate at least all legal captures including (under)promotions + end = gen_captures(pos, m); + Pos pos0 = *pos; + + // Resolve captures, letting bestCap keep track of the best non-ep + // capture and letting bestEp keep track of the best ep capture. + Pos pos1; + for (; m < end; m++) { + TbMove move = *m; + if (!is_capture(pos, move)) + continue; + if (!do_move(&pos1, pos, move)) + continue; + if (wdl < 0) + v = -probe_dtm_dc(&pos1, 1, success) + 1; + else if (probe_ab(&pos1, -1, 0, success) < 0 && *success) + v = -probe_dtm_dc(&pos1, 0, success) - 1; + else + v = -TB_VALUE_MATE; + if (is_en_passant(&pos1, move)) + bestEp = max(bestEp,v); + else + bestCap = max(bestCap,v); + if (*success == 0) + return 0; + } + + // If there are en passant captures, we have to determine the WDL value + // for the position without ep rights if it might be different. + if (bestEp > -TB_VALUE_INFINITE && (bestEp < 0 || bestCap < 0)) { + assert(ep_square() != 0); + uint8_t s = pos->st->epSquare; + pos->st->epSquare = 0; + wdl = probe_ab(pos, -2, 2, success); + pos->st->epSquare = s; + if (*success == 0) + return 0; + if (wdl == 0) + return bestEp; + } + + bestCap = max(bestCap,v); + int dtm = probe_dtm_table(pos, wdl > 0, success); + v = wdl > 0 ? TB_VALUE_MATE - 2 * dtm + 1 : -TB_VALUE_MATE + 2 * dtm; + return max(bestCap,v); +} +#endif + +static int WdlToDtz[] = { -1, -101, 0, 101, 1 }; + +// Probe the DTZ table for a particular position. +// If *success != 0, the probe was successful. +// The return value is from the point of view of the side to move: +// n < -100 : loss, but draw under 50-move rule +// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0) +// 0 : draw +// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0) +// 100 < n : win, but draw under 50-move rule +// +// If the position mate, -1 is returned instead of 0. +// +// The return value n can be off by 1: a return value -n can mean a loss +// in n+1 ply and a return value +n can mean a win in n+1 ply. This +// cannot happen for tables with positions exactly on the "edge" of +// the 50-move rule. +// +// This means that if dtz > 0 is returned, the position is certainly +// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine +// picks moves that preserve dtz + 50-move-counter <= 99. +// +// If n = 100 immediately after a capture or pawn move, then the position +// is also certainly a win, and during the whole phase until the next +// capture or pawn move, the inequality to be preserved is +// dtz + 50-movecounter <= 100. +// +// In short, if a move is available resulting in dtz + 50-move-counter <= 99, +// then do not accept moves leading to dtz + 50-move-counter == 100. +// +int probe_dtz(Pos *pos, int *success) +{ + int wdl = probe_wdl(pos, success); + if (*success == 0) return 0; + + // If draw, then dtz = 0. + if (wdl == 0) return 0; + + // Check for winning capture or en passant capture as only best move. + if (*success == 2) + return WdlToDtz[wdl + 2]; + + TbMove moves[TB_MAX_MOVES]; + TbMove *m = moves, *end = NULL; + Pos pos1; + + // If winning, check for a winning pawn move. + if (wdl > 0) { + // Generate at least all legal non-capturing pawn moves + // including non-capturing promotions. + // (The following call in fact generates all moves.) + end = gen_legal(pos, moves); + + for (m = moves; m < end; m++) { + TbMove move = *m; + if (type_of_piece_moved(pos,move) != PAWN || is_capture(pos, move)) + continue; + if (!do_move(&pos1, pos, move)) + continue; // not legal + int v = -probe_wdl(&pos1, success); + if (*success == 0) return 0; + if (v == wdl) { + assert(wdl < 3); + return WdlToDtz[wdl + 2]; + } + } + } + + // If we are here, we know that the best move is not an ep capture. + // In other words, the value of wdl corresponds to the WDL value of + // the position without ep rights. It is therefore safe to probe the + // DTZ table with the current value of wdl. + + int dtz = probe_dtz_table(pos, wdl, success); + if (*success >= 0) + return WdlToDtz[wdl + 2] + ((wdl > 0) ? dtz : -dtz); + + // *success < 0 means we need to probe DTZ for the other side to move. + int best; + if (wdl > 0) { + best = INT32_MAX; + } else { + // If (cursed) loss, the worst case is a losing capture or pawn move + // as the "best" move, leading to dtz of -1 or -101. + // In case of mate, this will cause -1 to be returned. + best = WdlToDtz[wdl + 2]; + // If wdl < 0, we still have to generate all moves. + end = gen_moves(pos, m); + } + assert(end != NULL); + + for (m = moves; m < end; m++) { + TbMove move = *m; + // We can skip pawn moves and captures. + // If wdl > 0, we already caught them. If wdl < 0, the initial value + // of best already takes account of them. + if (is_capture(pos, move) || type_of_piece_moved(pos, move) == PAWN) + continue; + if (!do_move(&pos1, pos, move)) { + // move was not legal + continue; + } + int v = -probe_dtz(&pos1, success); + // Check for the case of mate in 1 + if (v == 1 && is_mate(&pos1)) + best = 1; + else if (wdl > 0) { + if (v > 0 && v + 1 < best) + best = v + 1; + } else { + if (v - 1 < best) + best = v - 1; + } + if (*success == 0) return 0; + } + return best; +} + +// Use the DTZ tables to rank and score all root moves in the list. +// A return value of 0 means that not all probes were successful. +static int root_probe_dtz(const Pos *pos, bool hasRepeated, bool useRule50, struct TbRootMoves *rm) +{ + int v, success; + + // Obtain 50-move counter for the root position. + int cnt50 = pos->rule50; + + // The border between draw and win lies at rank 1 or rank 900, depending + // on whether the 50-move rule is used. + int bound = useRule50 ? 900 : 1; + + // Probe, rank and score each move. + TbMove rootMoves[TB_MAX_MOVES]; + TbMove * end = gen_legal(pos,rootMoves); + rm->size = (unsigned)(end-rootMoves); + Pos pos1; + for (unsigned i = 0; i < rm->size; i++) { + struct TbRootMove *m = &(rm->moves[i]); + m->move = rootMoves[i]; + do_move(&pos1, pos, m->move); + + // Calculate dtz for the current move counting from the root position. + if (pos1.rule50 == 0) { + // If the move resets the 50-move counter, dtz is -101/-1/0/1/101. + v = -probe_wdl(&pos1, &success); + assert(v < 3); + v = WdlToDtz[v + 2]; + } else { + // Otherwise, take dtz for the new position and correct by 1 ply. + v = -probe_dtz(&pos1, &success); + if (v > 0) v++; + else if (v < 0) v--; + } + // Make sure that a mating move gets value 1. + if (v == 2 && is_mate(&pos1)) { + v = 1; + } + + if (!success) return 0; + + // Better moves are ranked higher. Guaranteed wins are ranked equally. + // Losing moves are ranked equally unless a 50-move draw is in sight. + // Note that moves ranked 900 have dtz + cnt50 == 100, which in rare + // cases may be insufficient to win as dtz may be one off (see the + // comments before TB_probe_dtz()). + int r = v > 0 ? (v + cnt50 <= 99 && !hasRepeated ? 1000 : 1000 - (v + cnt50)) + : v < 0 ? (-v * 2 + cnt50 < 100 ? -1000 : -1000 + (-v + cnt50)) + : 0; + m->tbRank = r; + + // Determine the score to be displayed for this move. Assign at least + // 1 cp to cursed wins and let it grow to 49 cp as the position gets + // closer to a real win. + m->tbScore = r >= bound ? TB_VALUE_MATE - TB_MAX_MATE_PLY - 1 + : r > 0 ? max( 3, r - 800) * TB_VALUE_PAWN / 200 + : r == 0 ? TB_VALUE_DRAW + : r > -bound ? min(-3, r + 800) * TB_VALUE_PAWN / 200 + : -TB_VALUE_MATE + TB_MAX_MATE_PLY + 1; + } + return 1; +} + +// Use the WDL tables to rank all root moves in the list. +// This is a fallback for the case that some or all DTZ tables are missing. +// A return value of 0 means that not all probes were successful. +int root_probe_wdl(const Pos *pos, bool useRule50, struct TbRootMoves *rm) +{ + static int WdlToRank[] = { -1000, -899, 0, 899, 1000 }; + static Value WdlToValue[] = { + -TB_VALUE_MATE + TB_MAX_MATE_PLY + 1, + TB_VALUE_DRAW - 2, + TB_VALUE_DRAW, + TB_VALUE_DRAW + 2, + TB_VALUE_MATE - TB_MAX_MATE_PLY - 1 + }; + + int v, success; + + // Probe, rank and score each move. + TbMove moves[TB_MAX_MOVES]; + TbMove *end = gen_legal(pos,moves); + rm->size = (unsigned)(end-moves); + Pos pos1; + for (unsigned i = 0; i < rm->size; i++) { + struct TbRootMove *m = &rm->moves[i]; + m->move = moves[i]; + do_move(&pos1, pos, m->move); + v = -probe_wdl(&pos1, &success); + if (!success) return 0; + if (!useRule50) + v = v > 0 ? 2 : v < 0 ? -2 : 0; + m->tbRank = WdlToRank[v + 2]; + m->tbScore = WdlToValue[v + 2]; + } + + return 1; +} + +// Use the DTM tables to find mate scores. +// Either DTZ or WDL must have been probed successfully earlier. +// A return value of 0 means that not all probes were successful. +#if defined(__cplusplus) && __cplusplus >= 201703L +[[maybe_unused]] +#endif +int root_probe_dtm(const Pos *pos, struct TbRootMoves *rm) +{ + int success; + Value tmpScore[TB_MAX_MOVES]; + + // Probe each move. + for (unsigned i = 0; i < rm->size; i++) { + Pos pos1; + struct TbRootMove *m = &rm->moves[i]; + + // Use tbScore to find out if the position is won or lost. + int wdl = m->tbScore > TB_VALUE_PAWN ? 2 + : m->tbScore < -TB_VALUE_PAWN ? -2 : 0; + + if (wdl == 0) + tmpScore[i] = 0; + else { + // Probe and adjust mate score by 1 ply. + do_move(&pos1, pos, m->pv[0]); + Value v = -TB_probe_dtm(&pos1, -wdl, &success); + tmpScore[i] = wdl > 0 ? v - 1 : v + 1; + if (success == 0) + return 0; + } + } + + // All probes were successful. Now adjust TB scores and ranks. + for (unsigned i = 0; i < rm->size; i++) { + struct TbRootMove *m = &rm->moves[i]; + + m->tbScore = tmpScore[i]; + + // Let rank correspond to mate score, except for critical moves + // ranked 900, which we rank below all other mates for safety. + // By ranking mates above 1000 or below -1000, we let the search + // know it need not search those moves. + m->tbRank = m->tbRank == 900 ? 1001 : m->tbScore; + } + + return 1; +} + +// Use the DTM tables to complete a PV with mate score. +#if defined(__cplusplus) && __cplusplus >= 201703L +[[maybe_unused]] +#endif +void tb_expand_mate(Pos *pos, struct TbRootMove *move, Value moveScore, unsigned cardinalityDTM) +{ + int success = 1, chk = 0; + Value v = moveScore, w = 0; + int wdl = v > 0 ? 2 : -2; + + if (move->pvSize == TB_MAX_PLY) + return; + + Pos root = *pos; + // First get to the end of the incomplete PV. + for (unsigned i = 0; i < move->pvSize; i++) { + v = v > 0 ? -v - 1 : -v + 1; + wdl = -wdl; + Pos pos0 = *pos; + do_move(pos, &pos0, move->pv[i]); + } + + // Now try to expand until the actual mate. + if (popcount(pos->white | pos->black) <= cardinalityDTM) { + while (v != -TB_VALUE_MATE && move->pvSize < TB_MAX_PLY) { + v = v > 0 ? -v - 1 : -v + 1; + wdl = -wdl; + TbMove moves[TB_MAX_MOVES]; + TbMove *end = gen_legal(pos, moves); + TbMove *m = moves; + for (; m < end; m++) { + Pos pos1; + do_move(&pos1, pos, *m); + if (wdl < 0) + chk = probe_wdl(&pos1, &success); // verify that move wins + w = success && (wdl > 0 || chk < 0) + ? TB_probe_dtm(&pos1, wdl, &success) + : 0; + if (!success || v == w) break; + } + if (!success || v != w) + break; + move->pv[move->pvSize++] = *m; + Pos pos0 = *pos; + do_move(pos, &pos0, *m); + } + } + // Get back to the root position. + *pos = root; +} + +static const int wdl_to_dtz[] = +{ + -1, -101, 0, 101, 1 +}; + +// This supports the original Fathom root probe API +static uint16_t probe_root(Pos *pos, int *score, unsigned *results) +{ + int success; + int dtz = probe_dtz(pos, &success); + if (!success) + return 0; + + int16_t scores[MAX_MOVES]; + uint16_t moves0[MAX_MOVES]; + uint16_t *moves = moves0; + uint16_t *end = gen_moves(pos, moves); + size_t len = end - moves; + size_t num_draw = 0; + unsigned j = 0; + for (unsigned i = 0; i < len; i++) + { + Pos pos1; + if (!do_move(&pos1, pos, moves[i])) + { + scores[i] = SCORE_ILLEGAL; + continue; + } + int v = 0; + // print_move(pos,moves[i]); + if (dtz > 0 && is_mate(&pos1)) + v = 1; + else + { + if (pos1.rule50 != 0) + { + v = -probe_dtz(&pos1, &success); + if (v > 0) + v++; + else if (v < 0) + v--; + } + else + { + v = -probe_wdl(&pos1, &success); + v = wdl_to_dtz[v + 2]; + } + } + num_draw += (v == 0); + if (!success) + return 0; + scores[i] = v; + if (results != NULL) + { + unsigned res = 0; + res = TB_SET_WDL(res, dtz_to_wdl(pos->rule50, v)); + res = TB_SET_FROM(res, move_from(moves[i])); + res = TB_SET_TO(res, move_to(moves[i])); + res = TB_SET_PROMOTES(res, move_promotes(moves[i])); + res = TB_SET_EP(res, is_en_passant(pos, moves[i])); + res = TB_SET_DTZ(res, (v < 0? -v: v)); + results[j++] = res; + } + } + if (results != NULL) + results[j++] = TB_RESULT_FAILED; + if (score != NULL) + *score = dtz; + + // Now be a bit smart about filtering out moves. + if (dtz > 0) // winning (or 50-move rule draw) + { + int best = BEST_NONE; + uint16_t best_move = 0; + for (unsigned i = 0; i < len; i++) + { + int v = scores[i]; + if (v == SCORE_ILLEGAL) + continue; + if (v > 0 && v < best) + { + best = v; + best_move = moves[i]; + } + } + return (best == BEST_NONE? 0: best_move); + } + else if (dtz < 0) // losing (or 50-move rule draw) + { + int best = 0; + uint16_t best_move = 0; + for (unsigned i = 0; i < len; i++) + { + int v = scores[i]; + if (v == SCORE_ILLEGAL) + continue; + if (v < best) + { + best = v; + best_move = moves[i]; + } + } + return (best == 0? MOVE_CHECKMATE: best_move); + } + else // drawing + { + // Check for stalemate: + if (num_draw == 0) + return MOVE_STALEMATE; + + // Select a "random" move that preserves the draw. + // Uses calc_key as the PRNG. + size_t count = calc_key(pos, !pos->turn) % num_draw; + for (unsigned i = 0; i < len; i++) + { + int v = scores[i]; + if (v == SCORE_ILLEGAL) + continue; + if (v == 0) + { + if (count == 0) + return moves[i]; + count--; + } + } + return 0; + } +} + +#ifndef TB_NO_HELPER_API + +unsigned tb_pop_count(uint64_t bb) +{ + return popcount(bb); +} + +unsigned tb_lsb(uint64_t bb) +{ + return lsb(bb); +} + +uint64_t tb_pop_lsb(uint64_t bb) +{ + return poplsb(bb); +} + +uint64_t tb_king_attacks(unsigned sq) +{ + return king_attacks(sq); +} + +uint64_t tb_queen_attacks(unsigned sq, uint64_t occ) +{ + return queen_attacks(sq, occ); +} + +uint64_t tb_rook_attacks(unsigned sq, uint64_t occ) +{ + return rook_attacks(sq, occ); +} + +uint64_t tb_bishop_attacks(unsigned sq, uint64_t occ) +{ + return bishop_attacks(sq, occ); +} + +uint64_t tb_knight_attacks(unsigned sq) +{ + return knight_attacks(sq); +} + +uint64_t tb_pawn_attacks(unsigned sq, bool color) +{ + return pawn_attacks(sq, color); +} + +#endif /* TB_NO_HELPER_API */ diff --git a/src/fathom/tbprobe.h b/src/fathom/tbprobe.h new file mode 100644 index 0000000..82850e7 --- /dev/null +++ b/src/fathom/tbprobe.h @@ -0,0 +1,399 @@ +/* + * tbprobe.h + * (C) 2015 basil, all rights reserved, + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef TBPROBE_H +#define TBPROBE_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef TB_NO_STDINT +#include +#else +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned uint32_t; +typedef long long unsigned uint64_t; +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; +#endif + +#ifndef TB_NO_STDBOOL +#include +#else +#ifndef __cplusplus +typedef uint8_t bool; +#define true 1 +#define false 0 +#endif +#endif + +/* + * Internal definitions. Do not call these functions directly. + */ +extern bool tb_init_impl(const char *_path); +extern unsigned tb_probe_wdl_impl( + uint64_t _white, + uint64_t _black, + uint64_t _kings, + uint64_t _queens, + uint64_t _rooks, + uint64_t _bishops, + uint64_t _knights, + uint64_t _pawns, + unsigned _ep, + bool _turn); +extern unsigned tb_probe_root_impl( + uint64_t _white, + uint64_t _black, + uint64_t _kings, + uint64_t _queens, + uint64_t _rooks, + uint64_t _bishops, + uint64_t _knights, + uint64_t _pawns, + unsigned _rule50, + unsigned _ep, + bool _turn, + unsigned *_results); + +/****************************************************************************/ +/* MAIN API */ +/****************************************************************************/ + +#define TB_MAX_MOVES (192+1) +#define TB_MAX_CAPTURES 64 +#define TB_MAX_PLY 256 +#define TB_CASTLING_K 0x1 /* White king-side. */ +#define TB_CASTLING_Q 0x2 /* White queen-side. */ +#define TB_CASTLING_k 0x4 /* Black king-side. */ +#define TB_CASTLING_q 0x8 /* Black queen-side. */ + +#define TB_LOSS 0 /* LOSS */ +#define TB_BLESSED_LOSS 1 /* LOSS but 50-move draw */ +#define TB_DRAW 2 /* DRAW */ +#define TB_CURSED_WIN 3 /* WIN but 50-move draw */ +#define TB_WIN 4 /* WIN */ + +#define TB_PROMOTES_NONE 0 +#define TB_PROMOTES_QUEEN 1 +#define TB_PROMOTES_ROOK 2 +#define TB_PROMOTES_BISHOP 3 +#define TB_PROMOTES_KNIGHT 4 + +#define TB_RESULT_WDL_MASK 0x0000000F +#define TB_RESULT_TO_MASK 0x000003F0 +#define TB_RESULT_FROM_MASK 0x0000FC00 +#define TB_RESULT_PROMOTES_MASK 0x00070000 +#define TB_RESULT_EP_MASK 0x00080000 +#define TB_RESULT_DTZ_MASK 0xFFF00000 +#define TB_RESULT_WDL_SHIFT 0 +#define TB_RESULT_TO_SHIFT 4 +#define TB_RESULT_FROM_SHIFT 10 +#define TB_RESULT_PROMOTES_SHIFT 16 +#define TB_RESULT_EP_SHIFT 19 +#define TB_RESULT_DTZ_SHIFT 20 + +#define TB_GET_WDL(_res) \ + (((_res) & TB_RESULT_WDL_MASK) >> TB_RESULT_WDL_SHIFT) +#define TB_GET_TO(_res) \ + (((_res) & TB_RESULT_TO_MASK) >> TB_RESULT_TO_SHIFT) +#define TB_GET_FROM(_res) \ + (((_res) & TB_RESULT_FROM_MASK) >> TB_RESULT_FROM_SHIFT) +#define TB_GET_PROMOTES(_res) \ + (((_res) & TB_RESULT_PROMOTES_MASK) >> TB_RESULT_PROMOTES_SHIFT) +#define TB_GET_EP(_res) \ + (((_res) & TB_RESULT_EP_MASK) >> TB_RESULT_EP_SHIFT) +#define TB_GET_DTZ(_res) \ + (((_res) & TB_RESULT_DTZ_MASK) >> TB_RESULT_DTZ_SHIFT) + +#define TB_SET_WDL(_res, _wdl) \ + (((_res) & ~TB_RESULT_WDL_MASK) | \ + (((_wdl) << TB_RESULT_WDL_SHIFT) & TB_RESULT_WDL_MASK)) +#define TB_SET_TO(_res, _to) \ + (((_res) & ~TB_RESULT_TO_MASK) | \ + (((_to) << TB_RESULT_TO_SHIFT) & TB_RESULT_TO_MASK)) +#define TB_SET_FROM(_res, _from) \ + (((_res) & ~TB_RESULT_FROM_MASK) | \ + (((_from) << TB_RESULT_FROM_SHIFT) & TB_RESULT_FROM_MASK)) +#define TB_SET_PROMOTES(_res, _promotes) \ + (((_res) & ~TB_RESULT_PROMOTES_MASK) | \ + (((_promotes) << TB_RESULT_PROMOTES_SHIFT) & TB_RESULT_PROMOTES_MASK)) +#define TB_SET_EP(_res, _ep) \ + (((_res) & ~TB_RESULT_EP_MASK) | \ + (((_ep) << TB_RESULT_EP_SHIFT) & TB_RESULT_EP_MASK)) +#define TB_SET_DTZ(_res, _dtz) \ + (((_res) & ~TB_RESULT_DTZ_MASK) | \ + (((_dtz) << TB_RESULT_DTZ_SHIFT) & TB_RESULT_DTZ_MASK)) + +#define TB_RESULT_CHECKMATE TB_SET_WDL(0, TB_WIN) +#define TB_RESULT_STALEMATE TB_SET_WDL(0, TB_DRAW) +#define TB_RESULT_FAILED 0xFFFFFFFF + +/* + * The tablebase can be probed for any position where #pieces <= TB_LARGEST. + */ +extern unsigned TB_LARGEST; + +/* + * Initialize the tablebase. + * + * PARAMETERS: + * - path: + * The tablebase PATH string. + * + * RETURN: + * - true=success, false=failed. The TB_LARGEST global will also be + * initialized. If no tablebase files are found, then `true' is returned + * and TB_LARGEST is set to zero. + */ +bool tb_init(const char *_path); + +/* + * Free any resources allocated by tb_init + */ +void tb_free(void); + +/* + * Probe the Win-Draw-Loss (WDL) table. + * + * PARAMETERS: + * - white, black, kings, queens, rooks, bishops, knights, pawns: + * The current position (bitboards). + * - rule50: + * The 50-move half-move clock. + * - castling: + * Castling rights. Set to zero if no castling is possible. + * - ep: + * The en passant square (if exists). Set to zero if there is no en passant + * square. + * - turn: + * true=white, false=black + * + * RETURN: + * - One of {TB_LOSS, TB_BLESSED_LOSS, TB_DRAW, TB_CURSED_WIN, TB_WIN}. + * Otherwise returns TB_RESULT_FAILED if the probe failed. + * + * NOTES: + * - Engines should use this function during search. + * - This function is thread safe assuming TB_NO_THREADS is disabled. + */ +static inline unsigned tb_probe_wdl( + uint64_t _white, + uint64_t _black, + uint64_t _kings, + uint64_t _queens, + uint64_t _rooks, + uint64_t _bishops, + uint64_t _knights, + uint64_t _pawns, + unsigned _rule50, + unsigned _castling, + unsigned _ep, + bool _turn) +{ + if (_castling != 0) + return TB_RESULT_FAILED; + if (_rule50 != 0) + return TB_RESULT_FAILED; + return tb_probe_wdl_impl(_white, _black, _kings, _queens, _rooks, + _bishops, _knights, _pawns, _ep, _turn); +} + +/* + * Probe the Distance-To-Zero (DTZ) table. + * + * PARAMETERS: + * - white, black, kings, queens, rooks, bishops, knights, pawns: + * The current position (bitboards). + * - rule50: + * The 50-move half-move clock. + * - castling: + * Castling rights. Set to zero if no castling is possible. + * - ep: + * The en passant square (if exists). Set to zero if there is no en passant + * square. + * - turn: + * true=white, false=black + * - results (OPTIONAL): + * Alternative results, one for each possible legal move. The passed array + * must be TB_MAX_MOVES in size. + * If alternative results are not desired then set results=NULL. + * + * RETURN: + * - A TB_RESULT value comprising: + * 1) The WDL value (TB_GET_WDL) + * 2) The suggested move (TB_GET_FROM, TB_GET_TO, TB_GET_PROMOTES, TB_GET_EP) + * 3) The DTZ value (TB_GET_DTZ) + * The suggested move is guaranteed to preserved the WDL value. + * + * Otherwise: + * 1) TB_RESULT_STALEMATE is returned if the position is in stalemate. + * 2) TB_RESULT_CHECKMATE is returned if the position is in checkmate. + * 3) TB_RESULT_FAILED is returned if the probe failed. + * + * If results!=NULL, then a TB_RESULT for each legal move will be generated + * and stored in the results array. The results array will be terminated + * by TB_RESULT_FAILED. + * + * NOTES: + * - Engines can use this function to probe at the root. This function should + * not be used during search. + * - DTZ tablebases can suggest unnatural moves, especially for losing + * positions. Engines may prefer to traditional search combined with WDL + * move filtering using the alternative results array. + * - This function is NOT thread safe. For engines this function should only + * be called once at the root per search. + */ +static inline unsigned tb_probe_root( + uint64_t _white, + uint64_t _black, + uint64_t _kings, + uint64_t _queens, + uint64_t _rooks, + uint64_t _bishops, + uint64_t _knights, + uint64_t _pawns, + unsigned _rule50, + unsigned _castling, + unsigned _ep, + bool _turn, + unsigned *_results) +{ + if (_castling != 0) + return TB_RESULT_FAILED; + return tb_probe_root_impl(_white, _black, _kings, _queens, _rooks, + _bishops, _knights, _pawns, _rule50, _ep, _turn, _results); +} + +typedef uint16_t TbMove; + +#define TB_MOVE_FROM(move) \ + (((move) >> 6) & 0x3F) +#define TB_MOVE_TO(move) \ + ((move) & 0x3F) +#define TB_MOVE_PROMOTES(move) \ + (((move) >> 12) & 0x7) + +struct TbRootMove { + TbMove move; + TbMove pv[TB_MAX_PLY]; + unsigned pvSize; + int32_t tbScore, tbRank; +}; + +struct TbRootMoves { + unsigned size; + struct TbRootMove moves[TB_MAX_MOVES]; +}; + +/* + * Use the DTZ tables to rank and score all root moves. + * INPUT: as for tb_probe_root + * OUTPUT: TbRootMoves structure is filled in. This contains + * an array of TbRootMove structures. + * Each structure instance contains a rank, a score, and a + * predicted principal variation. + * RETURN VALUE: + * non-zero if ok, 0 means not all probes were successful + * + */ +int tb_probe_root_dtz( + uint64_t _white, + uint64_t _black, + uint64_t _kings, + uint64_t _queens, + uint64_t _rooks, + uint64_t _bishops, + uint64_t _knights, + uint64_t _pawns, + unsigned _rule50, + unsigned _castling, + unsigned _ep, + bool _turn, + bool hasRepeated, + bool useRule50, + struct TbRootMoves *_results); + +/* +// Use the WDL tables to rank and score all root moves. +// This is a fallback for the case that some or all DTZ tables are missing. + * INPUT: as for tb_probe_root + * OUTPUT: TbRootMoves structure is filled in. This contains + * an array of TbRootMove structures. + * Each structure instance contains a rank, a score, and a + * predicted principal variation. + * RETURN VALUE: + * non-zero if ok, 0 means not all probes were successful + * + */ +int tb_probe_root_wdl(uint64_t _white, + uint64_t _black, + uint64_t _kings, + uint64_t _queens, + uint64_t _rooks, + uint64_t _bishops, + uint64_t _knights, + uint64_t _pawns, + unsigned _rule50, + unsigned _castling, + unsigned _ep, + bool _turn, + bool useRule50, + struct TbRootMoves *_results); + +/****************************************************************************/ +/* HELPER API */ +/****************************************************************************/ + +/* + * The HELPER API provides some useful additional functions. It is optional + * and can be disabled by defining TB_NO_HELPER_API. Engines should disable + * the HELPER API. + */ + +#ifndef TB_NO_HELPER_API + +extern unsigned tb_pop_count(uint64_t _bb); +extern unsigned tb_lsb(uint64_t _bb); +extern uint64_t tb_pop_lsb(uint64_t _bb); +extern uint64_t tb_king_attacks(unsigned _square); +extern uint64_t tb_queen_attacks(unsigned _square, uint64_t _occ); +extern uint64_t tb_rook_attacks(unsigned _square, uint64_t _occ); +extern uint64_t tb_bishop_attacks(unsigned _square, uint64_t _occ); +extern uint64_t tb_knight_attacks(unsigned _square); +extern uint64_t tb_pawn_attacks(unsigned _square, bool _color); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/main.c b/src/main.c index 1247311..01c8d26 100755 --- a/src/main.c +++ b/src/main.c @@ -295,7 +295,7 @@ Return value: g_Options.fNoInputThread = FALSE; g_Options.fVerbosePosting = TRUE; strcpy(g_Options.szEGTBPath, "/zscratch/egtb"); - strcpy(g_Options.szLogfile, "typhoon.log"); + strcpy(g_Options.szLogfile, "/usr/local/tmp/typhoon.log"); strcpy(g_Options.szBookName, "book.bin"); g_Options.uNumHashTableEntries = 0x10000; g_Options.uNumProcessors = 1; @@ -356,9 +356,9 @@ Return value: { g_Options.fNoInputThread = TRUE; } - else if ((!STRCMPI(argv[i], "--dnafile")) && (argc > i)) + else if ((!STRCMPI(argv[i], "--dnafile")) && (argc > i)) { - if (!ReadEvalDNA(argv[i+1])) + if (!ReadEvalDNA(argv[i+1])) { UtilPanic(INITIALIZATION_FAILURE, (void *)"Bad DNA file", @@ -367,6 +367,10 @@ Return value: NULL, __FILE__, __LINE__); } + else + { + Trace("Loaded dna file \"%s\"\n", argv[i+1]); + } i++; } else if ((!STRCMPI(argv[i], "--logfile")) && (argc > i)) diff --git a/src/position.bin b/src/position.bin deleted file mode 100644 index 4216042..0000000 Binary files a/src/position.bin and /dev/null differ diff --git a/src/position.lrn b/src/position.lrn deleted file mode 100644 index 6d40222..0000000 --- a/src/position.lrn +++ /dev/null @@ -1,5 +0,0 @@ -[White "Crafty 19.13"] -[Black "unknown"] -[Date "2007.06.19"] -setboard r5k/1b1nq1bp/2p2rp/1p1p1pN/p2P3P/P2NP1P/1PQ2P1K/2R1RB/ w -{-149 6623 660} diff --git a/src/suite_diff.pl b/src/suite_diff.pl index 6749f07..2ae8a19 100755 --- a/src/suite_diff.pl +++ b/src/suite_diff.pl @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/env perl $verbose = 0; for $arg (@ARGV) { diff --git a/src/trial.ep_ b/src/trial.ep_ deleted file mode 100644 index a98e32f..0000000 --- a/src/trial.ep_ +++ /dev/null @@ -1,3520 +0,0 @@ -st 20 -easy -setboard 2q1r1k1/1ppb4/r2p1Pp1/p4n1p/2P1n3/5NPP/PP3Q1K/2BRRB2 w - - -solution f7+ -id "ECM.001" -go -setboard 7r/1p2k3/2bpp3/p3np2/P1PR4/2N2PP1/1P4K1/3B4 b - - -solution Bxf3+ -id "ECM.002" -go -setboard 4k3/p1P3p1/2q1np1p/3N4/8/1Q3PP1/6KP/8 w - - -solution Qb5 -id "ECM.003" -go -setboard 2r1b1k1/R4pp1/4pb1p/1pBr4/1Pq2P2/3N4/2PQ2PP/5RK1 b - - -solution Rcxc5 -id "ECM.004" -go -setboard 6k1/p1qb1p1p/1p3np1/2b2p2/2B5/2P3N1/PP2QPPP/4N1K1 b - - -solution Bxf2+ -id "ECM.005" -go -setboard 3q4/pp3pkp/5npN/2bpr1B1/4r3/2P2Q2/PP3PPP/R4RK1 w - - -solution Bxf6+ -id "ECM.006" -go -setboard 3rr1k1/pb3pp1/1p1q1b1p/1P2NQ2/3P4/P1NB4/3K1P1P/2R3R1 w - - -solution Rxg7+ -id "ECM.007" -go -setboard r1b1r1k1/p1p3pp/2p2n2/2bp4/5P2/3BBQPq/PPPK3P/R4N1R b - - -solution Bg4 -id "ECM.008" -go -setboard 3r4/1b2k3/1pq1pp2/p3n1pr/2P5/5PPN/PP1N1QP1/R2R2K1 b - - -solution Rxh3 -id "ECM.009" -go -setboard 2r4k/pB4bp/6p1/6q1/1P1n4/2N5/P4PPP/2R1Q1K1 b - - -solution Qxc1 -id "ECM.010" -go -setboard 1r5r/3b1pk1/3p1np1/p1qPp3/p1N1PbP1/2P2PN1/1PB1Q1K1/R3R3 b - - -solution Nxg4 -id "ECM.011" -go -setboard 5rk1/7p/p1N5/3pNp2/2bPnqpQ/P7/1P3PPP/4R1K1 w - - -solution Ne7+ -id "ECM.012" -go -setboard rnb2rk1/pp2np1p/2p2q1b/8/2BPPN2/2P2Q2/PP4PP/R1B2RK1 w - - -solution Nd5 -id "ECM.013" -go -setboard 2k4r/1pp2ppp/p1p1bn2/4N3/1q1rP3/2N1Q3/PPP2PPP/R4RK1 w - - -solution Nd5 -id "ECM.014" -go -setboard r3kb1r/pp2pppp/3q4/3Pn3/6b1/2N1BN2/PP3PPP/R2QKB1R w KQkq - -solution Nxe5 -id "ECM.015" -go -setboard 2rr2k1/1b3p1p/p4qpb/2R1n3/3p4/BP2P3/P3QPPP/3R1BKN b - - -solution Rxc5 -id "ECM.016" -go -setboard r1b1k3/5p1p/p1p5/3np3/1b2N3/4B3/PPP1BPrP/2KR3R w q - -solution Rxd5 -id "ECM.017" -go -setboard r3rbk1/1pq2ppp/2ppbnn1/p3p3/P1PPN3/BP1BPN1P/2Q2PP1/R2R2K1 w - - -solution Nxd6 -id "ECM.018" -go -setboard b7/2q2kp1/p3pbr1/1pPpP2Q/1P1N3P/6P1/P7/5RK1 w - - -solution Rxf6+ -id "ECM.019" -go -setboard 1rr1nbk1/5ppp/3p4/1q1PpN2/np2P3/5Q1P/P1BB1PP1/2R1R1K1 w - - -solution Bxa4 -id "ECM.020" -go -setboard r7/5kp1/2p1p2p/1p1n3P/2rP4/2P3R1/PK2RPP1/2B5 b - - -solution Rxc3 -id "ECM.021" -go -setboard r1r3k1/p3qpp1/b1P4p/3p4/3Nn3/4P3/P1Q2PPP/1BR1K2R b K - -solution Qb4+ -id "ECM.022" -go -setboard rn3rk1/4bppp/1q2p3/p2pP3/8/1PN2B1P/P4PP1/2RQ1RK1 w - - -solution Bxd5 -id "ECM.023" -go -setboard 6rk/1p1br2p/pqp5/3pNP2/3Pp3/P5PR/5PKR/Q7 w - - -solution Rxh7+ -id "ECM.024" -go -setboard 2b3k1/5p1p/7b/p2B3p/3P4/P2Q1N1P/1q3PP1/6K1 w - - -solution Bxf7+ -id "ECM.025" -go -setboard r1b5/4k3/p7/3p1n2/3Bp3/2P2r1P/PPBK1P2/4R2R w - - -solution Bc5+ -id "ECM.026" -go -setboard r4rk1/1b3Npp/p7/1p3Q2/3P4/1B2q3/P5PP/3n1R1K b - - -solution Bxg2+ -id "ECM.027" -go -setboard r1b2rk1/2q2ppp/2pbp3/p7/4Nn2/3B4/PPPBQ1PP/R4R1K w - - -solution Rxf4 -id "ECM.028" -go -setboard 3r2k1/1q1P1ppp/r2R2n1/p4Q2/1ppB2R1/6P1/PP3PP1/6K1 w - - -solution Rgxg6 -id "ECM.029" -go -setboard r2q1rk1/p3b1pp/2p5/1pn5/1n1Bp1b1/1P6/PQ1PPP2/2RNKBNR b K - -solution Bxe2 -id "ECM.030" -go -setboard 3r2k1/p2r1p1p/1p2p1p1/q4n2/3P4/PQ1R3P/1P2NPP1/3R2K1 b - - -solution Nxd4 -id "ECM.031" -go -setboard 2bnr1k1/2q2ppp/p7/2p1b3/2N1B3/R7/1P2QPPP/2B3K1 w - - -solution Nxe5 -id "ECM.032" -go -setboard r1b2r1k/3nN2p/p2Q1p2/8/4PP2/1R6/q1PK2PP/4R3 w - - -solution Qd5 -id "ECM.033" -go -setboard 1nk1r1r1/pp2n1pp/4p3/q2pPp1N/b1pP1P2/B1P2R2/2P1B1PP/R2Q2K1 w - - -solution Nf6 -id "ECM.034" -go -setboard 2r2bk1/4qp2/3n2p1/2R1p1Np/2p1N3/r6P/1Q3PP1/3R2K1 w - - -solution Rxc8 -id "ECM.035" -go -setboard br1r2k1/1q4p1/p2ppb1p/2p2p2/2P1n3/Pn2P2P/QPR2PPB/2NRNBK1 b - - -solution Nxc1 -id "ECM.036" -go -setboard 2k4r/ppp1n1p1/2n2qb1/2N5/Q1P3p1/P2r3P/1P3PB1/R1B2RK1 w - - -solution Qb5 -id "ECM.037" -go -setboard 8/6bk/1B1R4/6p1/2bNP3/4KP2/1r6/8 b - - -solution Be5 -id "ECM.038" -go -setboard r1b1rnk1/1p3pb1/1qpp2p1/2n5/2PNP2p/1PN3PP/3R1PBK/BR1Q4 b - - -solution Rxa1 -id "ECM.039" -go -setboard r1b2rk1/pp2b3/2pn1n1p/3pNppq/3P4/BP1N2P1/P3PPBP/R1Q2RK1 w - - -solution Nxc6 -id "ECM.040" -go -setboard 5rk1/1b3ppp/p3p3/1p1q2N1/4n3/6P1/PP2PPBP/2Q2RK1 b - - -solution Nxg3 -id "ECM.041" -go -setboard 2kr3r/pppq2p1/1bn1R3/6N1/1P6/1QN1B1p1/P4PP1/R5K1 b - - -solution gxf2+ -id "ECM.042" -go -setboard 2rr4/5pk1/p1q3p1/3bpP1p/1p4nR/1N4Q1/PPP3PP/3NR2K b - - -solution Bxg2+ -id "ECM.043" -go -setboard r4rk1/pp4pp/2nqb3/3p4/8/2NB4/PP1Q1PPP/4RRK1 w - - -solution Bxh7+ -id "ECM.044" -go -setboard 3rk1r1/3b1p1p/p2BpQ2/8/2q5/2P5/PP3PPP/3R2K1 b - - -solution Rxg2+ -id "ECM.045" -go -setboard 3r1rk1/4pp1p/p1Q1b1p1/3N4/3b4/5B2/Pq3PPP/R4RK1 b - - -solution Bxd5 -id "ECM.046" -go -setboard 5bk1/1bqn1r1p/p3Q1p1/4p3/1PN1Pp2/P1N4P/5PP1/R1BR2K1 w - - -solution Qxf7+ -id "ECM.047" -go -setboard 3rkb1r/1p3p2/p1n1p3/q5pp/2PpP3/1P4P1/P1Q1BPKP/R2N3R b k - -solution d3 -id "ECM.048" -go -setboard 5rk1/1r1qbnnp/R2p2p1/1p1Pp3/1Pp1P1N1/2P1B1NP/5QP1/5R1K w - - -solution Qxf7+ -id "ECM.049" -go -setboard 2kr1b1R/1b3pp1/p2pp3/1pq3P1/2nQPP2/P1N5/1PP1BB2/1K6 w - - -solution Rxf8 -id "ECM.050" -go -setboard 1k1r1b1r/p1p1q3/2p1p3/4Ppp1/4nP2/P2BQ3/1PP3PB/2KR3R b - - -solution Rxh2 -id "ECM.051" -go -setboard r3nb1Q/p1q2kr1/2p3p1/2ppnpP1/1P3N2/4P3/PB1P4/1R2KB1R w K - -solution Qxf8+ -id "ECM.052" -go -setboard r1bq1rk1/pp3pbp/2pp1np1/2n3B1/2PNP3/2N2P2/PP1QB1PP/R4RK1 b - - -solution Nfxe4 -id "ECM.053" -go -setboard 1rb1qn2/1p2nrbk/p2p2p1/P2Pp1Pp/NP2P2P/2RNB3/2QKB3/1R6 w - - -solution Ba7 -id "ECM.054" -go -setboard 5r2/1b2k2p/3bp3/5PP1/Bn1Q3P/1N6/1Pr5/1K3R2 b - - -solution Rxb2+ -id "ECM.055" -go -setboard r1b1r1k1/pp4bp/5np1/2qPB3/8/6NP/PPP1Q1B1/R4R1K w - - -solution Rxf6 -id "ECM.056" -go -setboard r1b2r1k/1pq3bp/8/3Qp2n/1P1n1pp1/2NP2P1/3BPPBP/1R2NRK1 b - - -solution Qxc3 -id "ECM.057" -go -setboard 4qk2/pp3pp1/1nbr3p/2p5/5N2/2P1Q3/PPB2PPP/2K1R3 w - - -solution Ng6+ -id "ECM.058" -go -setboard 3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - -solution Rd5 -id "ECM.059" -go -setboard r4rk1/pp1b2pp/3bpq2/3p1p2/2PPn3/PP1B4/1BQN1PPP/R4RK1 b - - -solution Bxh2+ -id "ECM.060" -go -setboard r4rk1/1b3ppp/p2q1n2/1p2N3/3P4/1B6/P1Q2PPP/2R1R1K1 w - - -solution Nxf7 -id "ECM.061" -go -setboard 2rq1r2/pp3pnk/2n1b1pp/3pQ3/3P1N2/P1N5/1P3PPP/1BR2RK1 w - - -solution Bxg6+ -id "ECM.062" -go -setboard 4nk2/r1r1bppp/pN2p3/2np4/8/BPN3PP/P3PPK1/2RR4 w - - -solution Ncxd5 -id "ECM.063" -go -setboard r4r1k/2pqn1b1/p1bp2pp/1p2pn2/4N3/2PP1PPQ/PPB4P/R1B1RNK1 b - - -solution Nd4 -id "ECM.064" -go -setboard r1bQ1nk1/bp3ppp/p7/3B4/1P6/P3P1P1/2P4P/q1B2RK1 b - - -solution Bxe3+ -id "ECM.065" -go -setboard 3r1rk1/1b2bppp/2q1pn2/pp4Q1/3B1P2/PBN5/1PP3PP/R2R3K b - - -solution Rxd4 -id "ECM.066" -go -setboard 2nq2k1/2r3pp/p1p1rp2/PpQ1N3/1P1PR3/8/5PPP/2R3K1 w - - -solution Nf7 -id "ECM.067" -go -setboard r1b2rk1/1p2bppp/p3pn2/4B3/1qBR4/2N5/P3QPPP/R5K1 w - - -solution Bd3 -id "ECM.068" -go -setboard 3r4/p2nrpkp/2B1p3/2P2pP1/3R3Q/q7/6PP/6RK w - - -solution g6 -id "ECM.069" -go -setboard 5r1k/p6p/1p1p1qp1/2pQpr2/8/2PP2P1/PP2RPKP/5R2 b - - -solution e4 -id "ECM.070" -go -setboard b1n1r1k1/2q2p1p/p4Pp1/1pBP4/4B1P1/2P1Q3/P4K1P/4R3 w - - -solution Qh6 -id "ECM.071" -go -setboard 2r2rk1/5pbp/p5p1/3P2q1/1QbN4/5P2/P4BPP/2RR2K1 b - - -solution Bf1 -id "ECM.072" -go -setboard r1bqnrk1/pp2ppb1/1np3pp/4P1N1/5P2/2NBB3/PPP3PP/R2Q1RK1 w - - -solution Nh7 -id "ECM.073" -go -setboard 3nk3/3q1p2/1B3pr1/4p3/bP2P2p/r2B4/2Q2RPP/3R2K1 w - - -solution Bb5 -id "ECM.074" -go -setboard 3rnr1k/pp3ppp/4b3/2p1qNP1/P2pPR2/1P1P2Q1/2P3BP/5RK1 w - - -solution Nh6 -id "ECM.075" -go -setboard r1b2kr1/1p1p1pNp/p1n1p1p1/5P2/3QP3/2N5/PqP1B1PP/R4RK1 w - - -solution fxg6 -id "ECM.076" -go -setboard 5rk1/3r1p1p/3b2pQ/8/4q3/4B2P/3R1PP1/4R1K1 w - - -solution Rxd6 -id "ECM.077" -go -setboard 4rrk1/1bp2ppp/p1q2b1B/1pn2B2/4N1Q1/2P4P/PP3PP1/3RR1K1 w - - -solution Nxc5 -id "ECM.078" -go -setboard 2rnrbnk/3q1ppp/p1p5/3p1P2/1p1PPN2/4BQ1R/PP1N2PP/3R2K1 w - - -solution Ng6+ -id "ECM.079" -go -setboard 5r1k/pb2r1bp/1p2B1p1/n7/4qNQ1/4B3/P4PPP/2RR2K1 w - - -solution Rd4 -id "ECM.080" -go -setboard Q7/p1r1ppk1/1qP4p/2Rb2p1/8/3N2P1/4PP1P/6K1 b - - -solution Qb1+ -id "ECM.081" -go -setboard 1r3rk1/5pp1/1n2qn1p/1p1pPNb1/2pP2QP/2P5/1PB3P1/R1B1R1K1 w - - -solution exf6 -id "ECM.082" -go -setboard r2qr1k1/pp3pbp/3p1np1/2pP1bB1/2P1N3/3Q1PN1/PP4PP/R4RK1 b - - -solution Nxe4 -id "ECM.083" -go -setboard 4r1k1/1b4pp/p7/1prP3q/2P1B3/5P2/P3Q2P/3RR2K w - - -solution Bg6 -id "ECM.084" -go -setboard r1br2k1/ppb1qpp1/2P4p/2n1p3/4B3/P1N2N2/1P3PPP/2RQ1RK1 w - - -solution Nd5 -id "ECM.085" -go -setboard 2r3k1/1nqbrp2/p5pp/1p1Pp1N1/8/1P5P/P1B2PP1/2RQR1K1 w - - -solution Nxf7 -id "ECM.086" -go -setboard 2r1rbk1/1b3ppp/pp6/2q1pNP1/Pn1RP3/2N5/1PP2QBP/5R1K w - - -solution Nh6+ -id "ECM.087" -go -setboard r1b1rk2/3n1pbQ/2qp2p1/p2N2P1/2Bp3N/4P3/PP3PP1/2KR3R w - - -solution Nxg6+ -id "ECM.088" -go -setboard r1r2qkb/4pp1p/pBbp3P/3Nn1P1/4Q3/1B6/PPP5/1K1R2R1 w - - -solution g6 -id "ECM.089" -go -setboard r4k2/q4npp/P2P4/2p1R3/2N5/6PP/Q5K1/8 w - - -solution Re7 -id "ECM.090" -go -setboard rn1q1r2/4bpk1/p3p3/1pN1N1np/2pP4/4PpBb/PPQ4P/1B1RK1R1 w - - -solution Nc6 -id "ECM.091" -go -setboard 5rk1/6pp/8/2Q4q/N2pP3/4b1Pb/4PP1K/1R3R2 b - - -solution Bxf1+ -id "ECM.092" -go -setboard 2r4k/pp2Q1pp/5r2/P2P4/3Pb1P1/4Ppq1/1P1R3N/3R3K b - - -solution Qe1+ -id "ECM.093" -go -setboard 1r1rb1k1/2p3pp/p2q1p2/3PpP1Q/Pp1bP2N/1B5R/1P4PP/2B4K w - - -solution Qxh7+ -id "ECM.094" -go -setboard r1b4k/p2n1Bpp/1p1Q1n2/5p2/3P4/P3BqP1/1P3P1P/R4RK1 b - - -solution Bb7 -id "ECM.095" -go -setboard rn1r2k1/p4ppp/2p3b1/3PQ3/1B2Np1q/1B3P2/P1P3PP/4R1K1 w - - -solution Nf6+ -solution Qe8+ -id "ECM.096" -go -setboard 4r2k/5R1p/pp1Bq1pN/2p1P3/1n1b3Q/3P4/1P4KP/8 w - - -solution Rxh7+ -id "ECM.097" -go -setboard r2nk1r1/pb3q1p/4p3/3p2pQ/8/BP6/P4PPP/2R1R1K1 w q - -solution Rc7 -id "ECM.098" -go -setboard 6k1/1pqb1p2/5bpB/r2n4/4N1P1/1P3P2/1P1Q4/1K5R w - - -solution Qxd5 -id "ECM.099" -go -setboard rr4k1/2p1ppb1/3pbnpp/q1pP4/4P3/1PNNBP1P/P1KR2P1/2Q4R b - - -solution Qxc3+ -id "ECM.100" -go -setboard 1r3rk1/3Q2pp/p3p3/6B1/3np3/6R1/PqP1B2P/3K2R1 w - - -solution Qxg7+ -id "ECM.101" -go -setboard 2r5/p4Q2/k1bBp3/4P3/p1nq4/5B2/2Pr2P1/1R1RK3 w - - -solution Qb7+ -id "ECM.102" -go -setboard 5rk1/p1pb2pp/2p5/3p3q/2P3n1/1Q4BN/PP1Np1KP/R3R3 b - - -solution Qxh3+ -id "ECM.103" -go -setboard 2r1nk2/4qpbQ/4b3/1p2RpP1/4P3/p7/PBP5/1K5R w - - -solution Qxg7+ -id "ECM.104" -go -setboard 2kr4/1bpr3p/4R3/1pQ5/8/6B1/3q2PP/2R4K w - - -solution Qxc7+ -id "ECM.105" -go -setboard r4rk1/1pbb2pp/p1p1p2q/2Pp4/1P1Pn3/P3B2P/2Q1BPP1/R4RNK b - - -solution Qxe3 -id "ECM.106" -go -setboard r1k4r/ppp1b3/5Npp/4pb1Q/8/1B2B3/PqP2PPP/2RR2K1 w - - -solution Qxf5+ -id "ECM.107" -go -setboard 6r1/p4rkp/3RQ1n1/1pp1p1B1/2p1P3/2P3R1/P4qPK/8 w - - -solution Qxe5+ -id "ECM.108" -go -setboard 2kr1r2/1pp3pp/1n2p1b1/pN2B3/P1q5/1Q1p3P/5PP1/R4R1K w - - -solution Rac1 -id "ECM.109" -go -setboard 2kr1bnr/1ppq4/p3bp2/6pp/4p3/2N2NBP/PP3PP1/2RQ1RK1 w - - -solution Na4 -id "ECM.110" -go -setboard r2q2rk/p3bp1p/4b1p1/3pP2R/1p1B4/3BQ3/PPP3PP/5R1K w - - -solution Rxf7 -id "ECM.111" -go -setboard r1b1Q3/6pk/p6p/1pq1n3/4N3/8/PPP3PP/R6K b - - -solution Qc6 -id "ECM.112" -go -setboard q6r/pp3pkp/5np1/4Q3/4P3/6N1/P4PPP/5RK1 w - - -solution Nh5+ -id "ECM.113" -go -setboard r1b1rk2/pp3ppQ/1np5/4q3/2B4N/4P3/PP3PP1/2R2K1R w - - -solution Qg8+ -id "ECM.114" -go -setboard r1bn1rk1/pp3p1p/6p1/2bR2N1/2B2B2/q1P1P3/2Q2PPP/4K2R w K - -solution Nxh7 -id "ECM.115" -go -setboard r3r1k1/p2N1pp1/1pn1p3/q1n5/2PQ4/P3B3/4PPKP/R5R1 w - - -solution Nf6+ -id "ECM.116" -go -setboard 4qbk1/1R5n/p2pr1p1/2pQpNPp/P1P1P3/2P2P2/3B4/5K2 w - - -solution Ne7+ -id "ECM.117" -go -setboard 3qr1k1/1p1bppbp/p2p2p1/2rPn3/P2N4/4BP2/1PPQ2PP/R2R1BK1 w - - -solution Ne6 -id "ECM.118" -go -setboard 2b2r2/1p2q1k1/r3pppp/4n2P/p3N3/1B4Q1/PPP3P1/3R1R1K w - - -solution Rxf6 -id "ECM.119" -go -setboard 8/R5p1/6k1/pr2p1b1/4K3/7P/5P2/6R1 w - - -solution h4 -id "ECM.120" -go -setboard 5rk1/1b1r1p2/p1q1pQp1/1p2P2p/8/1BP1NR2/PP1n2PP/5R1K w - - -solution Bxe6 -id "ECM.121" -go -setboard 6k1/p1p2r2/1p1p1P1Q/3P4/2P5/5qr1/PP4RP/6RK w - - -solution Qh3 -id "ECM.122" -go -setboard 2b1rr1k/1p5p/1Ppp2q1/p3bN2/2P1n3/5R2/PBQ3BP/3R2K1 w - - -solution Nxd6 -id "ECM.123" -go -setboard 3b2rk/7p/p7/2pbqNrn/Pp1p1R2/1P1Q2P1/1BPN1R1P/6K1 w - - -solution Rxd4 -id "ECM.124" -go -setboard 1rbr2k1/1pq1bp1p/p1pNn1p1/2n1p3/2B1P2P/1NQ1BP2/PP4P1/1K1R3R w - - -solution Nxf7 -id "ECM.125" -go -setboard r4rk1/1pp1b1pp/p1q5/3pPp2/3Pn3/P3N3/1P3PPP/R1BQR1K1 w - - -solution Qb3 -id "ECM.126" -go -setboard rbbq1rk1/p3nppp/1p2p3/8/1B1pN3/P2B4/1P3PPP/2RQ1RK1 w - - -solution Nf6+ -id "ECM.127" -go -setboard rn2qbr1/2p4k/p2p1nb1/1p1Pp2p/2P4P/2NBBPN1/PP1Q4/2KR2R1 w - - -solution Nxh5 -id "ECM.128" -go -setboard 5r1k/1p2qp1p/p4R1Q/P1p5/2Prp3/7P/1P4P1/5R1K w - - -solution Re6 -id "ECM.129" -go -setboard r1bq1rk1/pp2bp1p/2p2np1/3p2B1/3P4/2NQ2N1/PPP2PPP/4RRK1 w - - -solution Rxe7 -id "ECM.130" -go -setboard 2rq1rk1/pp2bppp/1npp1n2/3PpN1b/2P5/2N3PP/PP2PPB1/R1BQ1RK1 w - - -solution c5 -id "ECM.131" -go -setboard rnbq1rk1/pp2b1pp/5n2/2pPp3/2P1N3/3B1N2/PP4PP/R1BQK2R w KQ - -solution d6 -id "ECM.132" -go -setboard 2n1rbk1/5pp1/7p/qP1pp3/4b1Q1/1B5P/1P1N1PP1/2B1R1K1 w - - -solution Rxe4 -id "ECM.133" -go -setboard 4k3/2R2p2/5p1p/8/rb6/1N6/5PPP/5K2 w - - -solution Rc4 -id "ECM.134" -go -setboard 2k3r1/pp2rp2/1np5/2Np1p2/P2P3p/1R2P1Pq/2Q2P1P/1R4K1 w - - -solution Nxb7 -id "ECM.135" -go -setboard 2r1k3/p3nrR1/1p1p4/2p1p1p1/2P1B3/2PP1P2/P5K1/7R w - - -solution Rh8+ -id "ECM.136" -go -setboard 8/1Qp3pk/7p/5p2/5Pqn/1P1r4/P5PP/2B2RK1 b - - -solution c6 -id "ECM.137" -go -setboard 2rr1bk1/5p1p/pPN2np1/3Bp3/2Q1n3/1P2B1Pq/P3PP2/R2R2K1 b - - -solution Rxd5 -id "ECM.138" -go -setboard 3r2k1/1p4pp/p1q1p3/3p1B2/bP1B2P1/P4Q2/5PKP/2b2R2 w - - -solution Rxc1 -id "ECM.139" -go -setboard rnb1r1k1/pp5p/2pp2p1/4b3/2P1P2q/1NN1BPp1/PP1QB1P1/3RR1K1 b - - -solution Bf4 -id "ECM.140" -go -setboard 5n1r/1p2kp2/rB1Nb3/4p1Pp/4p2P/1PP5/8/1K1R1R2 w - - -solution Rxf7+ -id "ECM.141" -go -setboard b3r3/q2B2k1/3Q2p1/1p5p/3pP3/5P2/1p4PP/5RK1 b - - -solution d3+ -id "ECM.142" -go -setboard 4r1k1/3R1ppp/p2p4/5PP1/2q2P2/2n2B2/P1P3QP/1r3R1K w - - -solution Rxb1 -id "ECM.143" -go -setboard 2r3k1/pp2n3/6pQ/4q3/8/2P1p1P1/P5BP/3R2K1 w - - -solution Bd5+ -id "ECM.144" -go -setboard 6k1/4ppbp/p5p1/1B1Q4/2P2Pn1/8/qr4PP/2B2RK1 b - - -solution Bd4+ -id "ECM.145" -go -setboard 8/p3q1kp/1p2Pnp1/3pQ3/2pP4/1nP3N1/1B4PP/6K1 w - - -solution Ba3 -id "ECM.146" -go -setboard r2b1rk1/5qp1/2p2P2/2p3Pp/p6P/2Q5/PPP5/2KR2R1 w - - -solution g6 -id "ECM.147" -go -setboard 5r2/1b3pk1/pqrpp1p1/4n1Nn/1p2PP1P/8/PPPQB3/1NKR2R1 b - - -solution Rfc8 -id "ECM.148" -go -setboard 1r2k2r/p5bp/4p1p1/q2pn3/1p2N1P1/6QP/PPP5/1KBR3R w k - -solution Bh6 -id "ECM.149" -go -setboard rn1q1rk1/pp3p1p/2p5/3p4/5Q1P/2NP3R/PPP3P1/4RK2 w - - -solution Rg3+ -id "ECM.150" -go -setboard 7r/kppQP1pp/p7/2qp1R2/4n3/1P2b2P/P1P3PK/R7 b - - -solution Re8 -id "ECM.151" -go -setboard r1b1kb1Q/qp2pp2/p3n1p1/2nN2Pp/2B4P/7N/PPP5/2KRR3 w q - -solution Nc7+ -id "ECM.152" -go -setboard 8/1bq3kp/p5p1/1pb1p3/4P2P/2PQ2P1/1P5B/5N1K b - - -solution Qd8 -id "ECM.153" -go -setboard 2r1q1r1/6bk/p2pN1pp/1p1N4/4P3/3R3Q/PnP4P/1K4R1 w - - -solution Nf6+ -id "ECM.154" -go -setboard 4r3/Q2bppkp/3p2p1/3Nn3/4P3/6PP/1qr2PB1/R3R1K1 b - - -solution Ra8 -id "ECM.155" -go -setboard 6k1/p1q2pp1/2n2b2/3B2Pp/2P2P2/5N1P/Pr1Q2K1/4R3 w - - -solution Re8+ -id "ECM.156" -go -setboard r1r3k1/p4n2/3p4/5NpR/8/2q2P2/P1PQ4/2K4R w - - -solution Rh8+ -id "ECM.157" -go -setboard 8/p5Q1/2ppq2p/3n1ppk/3B4/2P2P1P/P5P1/6K1 w - - -solution g4+ -id "ECM.158" -go -setboard r1r3k1/1p1b1nq1/6p1/pN1p4/P2Np2R/1P2P3/1Q3PP1/1K5R w - - -solution Nd6 -id "ECM.159" -go -setboard 1r2k2r/3q2pp/p3pp2/P7/2P1Q3/8/1nB3PP/1R3R1K w k - -solution Ba4 -id "ECM.160" -go -setboard r1bq3k/ppp2Qp1/2n1p2p/6N1/2BPN3/2P1n3/PP4PP/R5K1 w - - -solution Nf6 -id "ECM.161" -go -setboard 3R4/1p2kp2/p1b1nN2/6p1/8/6BP/2r1qPPK/Q7 w - - -solution Rd7+ -id "ECM.162" -go -setboard 6k1/p5p1/1p1p1nN1/1B1P4/4PK2/8/2r3b1/7R w - - -solution Rh8+ -id "ECM.163" -go -setboard 8/5P2/4K1kP/4R3/8/8/8/5r2 w - - -solution Rg5+ -id "ECM.164" -go -setboard 3r1rk1/p1p1qp1p/2p1b1p1/8/P2PRP2/2P1NQ2/6PP/R5K1 w - - -solution f5 -id "ECM.165" -go -setboard 6k1/pr3ppp/1p3qn1/5NQ1/2p5/8/P4PPP/4R1K1 w - - -solution Re8+ -id "ECM.166" -go -setboard Q7/1pq2kpp/4bp2/3r4/2B5/P3P3/1r3PPP/2R1K2R b K - -solution Rd1+ -id "ECM.167" -go -setboard 4r1k1/p4ppp/4q2r/1Q6/4p3/4P1P1/P4P1P/2RR2K1 w - - -solution Rd6 -id "ECM.168" -go -setboard 8/5p1k/4p1p1/3p2K1/3P1Q1P/P4P2/7q/8 b - - -solution f6+ -id "ECM.169" -go -setboard r4r2/6k1/3p4/2pPp2p/4Pqp1/3P1P2/1R2Q1KP/5R2 b - - -solution Ra1 -id "ECM.170" -go -setboard 4r1k1/1np1r1pp/1ppp1q2/6N1/1P3P2/4RQP1/7P/4R1K1 w - - -solution Qh5 -id "ECM.171" -go -setboard r4rk1/pp3p1p/3pq1p1/2nNp3/3R1P2/7Q/PP4PP/5RK1 w - - -solution f5 -id "ECM.172" -go -setboard 3qn2k/1p3Bp1/rbn4p/2p5/p1P2p2/P2b2PP/1BQNRP2/1R4K1 w - - -solution Qxd3 -id "ECM.173" -go -setboard 3r1rk1/1pp4p/1q3bp1/p3p3/P2n1P2/2BB3P/1PP2QP1/R4RK1 b - - -solution Bh4 -id "ECM.174" -go -setboard 4rrk1/2p2ppp/p2b4/1p1P4/3P2nq/2P4P/PPQ1RPP1/RNB3K1 b - - -solution Qxf2+ -id "ECM.175" -go -setboard 1q6/2rk1p2/p1n1b1rN/3p2p1/8/3Q4/PP1B1PPP/2R1R1K1 w - - -solution Nxf7 -id "ECM.176" -go -setboard r3kb1r/2q2p2/3p4/ppp1n1B1/P6P/1B3pQ1/1PP3b1/2KRR3 w kq - -solution Bd5 -id "ECM.177" -go -setboard r1b2rk1/1p4pp/p1n3q1/4ppN1/7Q/2P1B3/P1P2PPP/R2R2K1 w - - -solution Rd6 -id "ECM.178" -go -setboard 1rb2r2/1pR1N2p/p2R2pk/5p2/4pP2/1P1nP3/P4P1P/5BK1 w - - -solution Rf6 -id "ECM.179" -go -setboard r1b3k1/ppp2r2/6p1/3n3p/6qP/3BQ3/PBP5/2KR3R w - - -solution Bd4 -id "ECM.180" -go -setboard 3r2k1/p2q1r1p/1p1N2p1/1PpP4/3b1Bn1/1Q4Pb/1P2RP1P/R5KB b - - -solution Qxd6 -id "ECM.181" -go -setboard 4r1k1/1pbb3p/2pp2q1/p1n5/2P2R2/1PN1n1P1/PB2Q1B1/5RK1 w - - -solution Qxe3 -id "ECM.182" -go -setboard 3r2k1/p2r1p1p/1pR1p1qP/3P1pP1/3R4/1P2Q3/P4K2/8 w - - -solution dxe6 -id "ECM.183" -go -setboard 3r3k/p1Q3bp/2p3p1/8/2P1BRn1/1P4Pb/P1N1q2P/6BK b - - -solution Nf2+ -id "ECM.184" -go -setboard r4k2/p1pr1ppp/2q5/3PR2P/3Q4/8/6P1/5RK1 w - - -solution Re7 -id "ECM.185" -go -setboard 1R3b2/3r3k/2p1bp1p/r1q1pNpQ/2PpP2P/6B1/P4PP1/1R4K1 w - - -solution Nxh6 -id "ECM.186" -go -setboard r1b4r/pp2ppk1/2np1np1/3N3p/2B1P3/q3BP2/P1PQ2PP/1R3RK1 w - - -solution Nxe7 -id "ECM.187" -go -setboard r3k2r/pb1n1pbp/4p1p1/q1n1P3/Bp1N1N2/4B3/PP3PPP/R2Q1RK1 w kq - -solution Ndxe6 -id "ECM.188" -go -setboard 5rk1/Q2nqppp/5n2/2p5/8/1B2PP1P/Pr1B1P2/R3K2R b KQ - -solution Ne4 -id "ECM.189" -go -setboard 5rk1/5pbp/b5p1/p1nNN3/1p2n3/1P4PP/P4PB1/2BR2K1 w - - -solution Nd7 -id "ECM.190" -go -setboard 2k1r1r1/ppb2p2/6b1/2qP4/2P2P2/4pP1p/P3Q2P/2RBRN1K b - - -solution Bd3 -id "ECM.191" -go -setboard 5r1k/pp1n1p1p/1b1qpP2/8/1PrN4/P1N1Q1P1/7P/3R1R1K w - - -solution Qh6 -id "ECM.192" -go -setboard r2r1bk1/3qp2p/3pp1p1/p2n2N1/2N3Q1/BP4P1/P4PP1/2R3K1 w - - -solution Nb6 -id "ECM.193" -go -setboard 1r3rk1/p4pbp/q1p2pp1/3p1b2/1n1P1P1B/PB3N2/1PPQR1PP/2KR4 b - - -solution Bxc2 -id "ECM.194" -go -setboard 1rbr2k1/4qp2/p1n2bpp/1pp1p1N1/4P3/2P1BQ1P/PPB2PP1/3RR1K1 w - - -solution Bxc5 -id "ECM.195" -go -setboard 2r2rk1/1p1q1ppp/1p3n2/3p1N2/4n3/1N3Q2/PPP2PPP/R2R2K1 w - - -solution Rxd5 -id "ECM.196" -go -setboard rn3rk1/pp2ppbp/6p1/2R3N1/2B3b1/q3B3/P1P1Q1PP/5RK1 w - - -solution Rxf7 -id "ECM.197" -go -setboard r1k3r1/pppnb1p1/4N3/3QP2p/3P3q/4B3/PPP2P2/R3KB2 w Q - -solution Ba6 -id "ECM.198" -go -setboard r1nqr2k/1p1b1Q1p/p5p1/P1pPb3/5B2/2N5/BP4PP/R4RK1 w - - -solution Qf8+ -id "ECM.199" -go -setboard 8/k3r1b1/Pp2rpp1/1qpQ4/4nB2/2P2NP1/7P/R4RK1 w - - -solution Bb8+ -id "ECM.200" -go -setboard 1k1r4/ppq2p2/8/2pPb1r1/2P1Q1B1/6P1/PP3PK1/R4R2 b - - -solution f5 -id "ECM.201" -go -setboard r1b2nk1/2qn1p1p/1ppR2p1/8/1PP1NP2/4r1PP/1Q2N1B1/3R2K1 w - - -solution b5 -id "ECM.202" -go -setboard 8/5pk1/3q2p1/8/7P/5Q1K/6B1/6b1 b - - -solution Qh2+ -id "ECM.203" -go -setboard 6r1/ppp4k/3p2rp/3Pb3/PPP1Np1q/5P1b/3QRRBP/6K1 b - - -solution Bd4 -id "ECM.204" -go -setboard 4br1k/pR4bp/2r1B1pN/4npB1/3p4/P7/5PPP/4R1K1 w - - -solution Rxg7 -id "ECM.205" -go -setboard r1bq1rk1/pppn2bp/3p2p1/3N1p2/8/BP1BRQ1P/P1PP1PP1/4R1K1 w - - -solution Nxc7 -id "ECM.206" -go -setboard r6r/1p2kp2/3p1b2/pPpPpQ1P/1nP1P2P/6R1/1P2K3/1B6 w - - -solution Rg6 -id "ECM.207" -go -setboard 2q1r1k1/1b3pbp/3p2p1/1Ppn3n/5P2/5B2/1P1B2PP/R2NNQ1K b - - -solution Ndxf4 -id "ECM.208" -go -setboard 2r1r1k1/pp3pp1/3p3B/P2P1P2/2nb4/7R/1q3PQP/1B3R1K w - - -solution Rb3 -id "ECM.209" -go -setboard r2qr1n1/p4pk1/1p1p2p1/4p3/4PQ2/1PN2P2/1PP3P1/2KR3R w - - -solution Rh7+ -id "ECM.210" -go -setboard r4rk1/ppp3b1/3p1qp1/3Pp3/2P1Bn2/4BP2/PP1Q4/2K3RR w - - -solution Rxg6 -id "ECM.211" -go -setboard 1k1r2r1/pp3p1p/B2q1n2/8/3Pb1p1/2Q5/PR3PPP/2B1R1K1 w - - -solution Bf4 -id "ECM.212" -go -setboard k3r3/pR5p/PppR1p1p/4nP2/1PP5/8/4B1rP/2K5 w - - -solution Re7 -id "ECM.213" -go -setboard 1q3rk1/r5b1/pNp1bn1p/2Pp1n2/1P2p3/1N2P2P/PBQKB3/3R3R b - - -solution Nxe3 -id "ECM.214" -go -setboard r4n1r/ppq1nk2/1bpRpN2/4PpQ1/8/2B4P/PPP2PP1/2KR4 w - - -solution Rd7 -id "ECM.215" -go -setboard 3r2k1/p5pp/bpP1p3/3n1p2/2NQpP1q/P3P3/1B1R1R2/4KBr1 b - - -solution Rxf1+ -id "ECM.216" -go -setboard 1rq1r2k/5Rbp/p2p1p1B/2p1p3/2P1P2Q/1P6/P5PP/3b3K w - - -solution Bxg7+ -id "ECM.217" -go -setboard 5Q2/5p1p/3p2p1/4p1k1/4P1P1/3R1P1K/r6P/4q3 w - - -solution f4+ -id "ECM.218" -go -setboard 2b2r1k/ppq3pp/3b3r/n3NpN1/Q2PpP1B/2P1R3/PP5P/4R1K1 w - - -solution Ngf7+ -id "ECM.219" -go -setboard 2r1r1k1/1R4bp/p5p1/2pqn3/8/2B5/1PQ1B1PP/5RK1 w - - -solution Bc4 -id "ECM.220" -go -setboard 7k/pp1q2pp/3Nr1n1/3B2Q1/2pP4/B3P1Pb/P6P/6K1 w - - -solution Qd8+ -id "ECM.221" -go -setboard r5k1/5p1p/bq1p2p1/r2Pp3/1p2N1P1/1P3P2/1KPQ3P/3R3R b - - -solution Ra2+ -id "ECM.222" -go -setboard 5Bk1/pr2pp1p/2b3pQ/2p1q3/8/2P4P/PP4P1/1B1Rb1K1 w - - -solution Bg7 -id "ECM.223" -go -setboard 5nk1/2b1q1p1/2p5/3pP1pQ/1r1P2P1/3B1R2/5PP1/6K1 w - - -solution Rxf8+ -id "ECM.224" -go -setboard 1k1r3r/1pp2p2/p2b4/4n1p1/N3q1P1/1B2B3/PP3RP1/4RQK1 b - - -solution Rh1+ -id "ECM.225" -go -setboard rnb1kr2/p4pQ1/8/1ppPpP2/2p1P3/N6P/Pq4B1/3R1RK1 w q - -solution Nxb5 -id "ECM.226" -go -setboard 5r1k/2p3pp/3pQ3/3P1rN1/pp5q/5PR1/P4PKP/B7 w - - -solution Bxg7+ -id "ECM.227" -go -setboard 8/pR3Bk1/3p2p1/5b1p/2P4P/6P1/P4bQK/4q3 b - - -solution Bg1+ -id "ECM.228" -go -setboard r1b1Rnk1/6pp/p4q2/1pp2P2/3r4/2NB4/PP1Q2PP/4R1K1 w - - -solution Nd5 -id "ECM.229" -go -setboard 1Q6/pb3pk1/1p3qp1/1N3p1p/2Pr3P/3BR1PK/r7/6R1 b - - -solution Rh2+ -id "ECM.230" -go -setboard rq3r1k/p2n1p2/1pbb1np1/2p2BB1/8/2N2N2/PP3QPP/3R1RK1 w - - -solution Rxd6 -id "ECM.231" -go -setboard 2kr3r/Qpp1n1p1/2b2pp1/4b3/8/6N1/PPP2P2/R1B2RK1 b - - -solution Rh1+ -id "ECM.232" -go -setboard 8/2p2Q1p/1rp5/p3q1kp/P7/2P2RP1/5PK1/8 w - - -solution Rf5+ -id "ECM.233" -go -setboard 1r2b3/1p2q1bk/p1p1p1p1/P1PpP2r/1P1KpPQP/4P2N/6R1/4B2R b - - -solution Rxe5 -id "ECM.234" -go -setboard 6k1/3b1p1p/6p1/2BPR3/8/4Q2P/1q3rPK/8 w - - -solution Re8+ -id "ECM.235" -go -setboard 2R5/4ppkp/6p1/p2P4/4P2P/p3B1P1/qbR2PK1/8 w - - -solution Rg8+ -id "ECM.236" -go -setboard 3rr1k1/1b5p/p6p/1p3N2/6Pb/P1Q1p3/1PP4P/R5K1 b - - -solution Bf2+ -id "ECM.237" -go -setboard 6k1/p4pq1/2n1p1p1/1p1pP1N1/3P1QPP/8/P3K3/8 w - - -solution Qc1 -id "ECM.238" -go -setboard 3r1rk1/1pq1nppp/p7/2pB3Q/P4P2/1P2P3/6PP/2RR2K1 w - - -solution Bf3 -solution Rxc5 -id "ECM.239" -go -setboard 8/p1r2q1k/1p1p3p/1Pp1nP2/P1P5/3R2PP/1Q4BK/8 w - - -solution Rxd6 -id "ECM.240" -go -setboard 6k1/1pp2p2/p2p2q1/2PPb3/4r3/Pr4PK/2R5/2Q2NR1 b - - -solution Rh4+ -id "ECM.241" -go -setboard 3r4/1pQ4R/p1b2kp1/P6p/2q5/7P/2P3P1/6K1 b - - -solution Rd1+ -id "ECM.242" -go -setboard 6rr/1b3k1p/pbqppPn1/1p5Q/4PN2/P6R/1PP3BP/2B2R1K w - - -solution Qxh7+ -id "ECM.243" -go -setboard 4rrk1/3b1ppp/pb6/1pqP4/P1p1PP2/1P2NK2/3QB2P/R2R4 b - - -solution Rxe4 -id "ECM.244" -go -setboard 2r1r1k1/1p4bp/p2p1qp1/1n1P4/2P1B2P/1N1Q4/PP2R3/1K5R b - - -solution Rxc4 -id "ECM.245" -go -setboard 6rr/ppqb1pkp/2pb1pn1/3p3Q/3P1P1N/3B1RN1/PPP3PP/5RK1 w - - -solution Qh6+ -id "ECM.246" -go -setboard r2qr1k1/1pnb1pp1/p1n1p2p/8/P2P3P/B2B1NP1/6P1/R2Q1RK1 w - - -solution Bh7+ -id "ECM.247" -go -setboard r2q1rk1/1b1nbpp1/p1p1p2p/1pPpN3/3P1N2/P2BP3/1PQ2PPP/R4RK1 w - - -solution Bh7+ -id "ECM.248" -go -setboard 2r2rk1/pb4pp/1p2p3/4Npq1/3P1n2/5P2/PP1R2PP/RB2Q1K1 b - - -solution Rc1 -id "ECM.249" -go -setboard 2r1rbk1/5pp1/bq5p/1pnBPN2/6Q1/N7/5PPP/R2R2K1 w - - -solution Bxf7+ -id "ECM.250" -go -setboard 1k1r1r2/2b2R2/Bppp2p1/2q4p/Q2pP3/P2P4/2P3PP/1R4K1 w - - -solution Bb7 -id "ECM.251" -go -setboard r5k1/pp2ppb1/4qn2/r3B1p1/3P4/3QNP1R/PP2K1P1/7R w - - -solution Bxf6 -id "ECM.252" -go -setboard 1R2br1k/5r1p/p2p1P2/6RQ/3p1P2/4p2B/q6P/5N1K w - - -solution Rxe8 -id "ECM.253" -go -setboard kr1r4/7p/2bqp1p1/p7/1Pp1PP1P/K1B1nNPB/PP5R/RN2Q3 b - - -solution Qxb4+ -id "ECM.254" -go -setboard 5r1r/1pkb4/5pB1/2P5/p3R2P/Pq6/1PQ2Pn1/K2R4 w - - -solution Rxd7+ -id "ECM.255" -go -setboard kn5r/p2r2p1/1pqBp1np/1Q1pPp2/R2P4/1N6/2P2PPP/R5K1 w - - -solution Rxa7+ -id "ECM.256" -go -setboard 4Rnk1/2p2pp1/3r3p/p2P4/P5Q1/1B5P/5PK1/q7 w - - -solution Rxf8+ -id "ECM.257" -go -setboard 4r3/2k1p2q/pp1nR1p1/2pP1pP1/2P2P2/1P6/P1K3B1/4Q3 w - - -solution Rxd6 -id "ECM.258" -go -setboard 2b3k1/p4ppp/7q/2Q5/8/P3r1P1/1r4BP/R3R1K1 b - - -solution Rxg2+ -id "ECM.259" -go -setboard r1b1rbk1/7p/2p1pBp1/p2pP1P1/5P2/1PqB4/P1P1Q3/1K4RR w - - -solution Rxh7 -id "ECM.260" -go -setboard 2rq4/5rbk/3p1n1p/p2Pp2P/1p2b3/4BNR1/PP1Q1P2/3BK1R1 w - - -solution Bxh6 -id "ECM.261" -go -setboard r2q1rkb/pp2p3/2p2pp1/3pPb2/3P3R/2N3P1/PPPQBPK1/3R4 w - - -solution Rxh8+ -id "ECM.262" -go -setboard 3r2k1/p4rPp/1b1q3Q/n1p1pP2/1p6/3B1NR1/P4P1P/6RK w - - -solution Qxh7+ -id "ECM.263" -go -setboard r1bqrnk1/p4p2/1p5p/3pB1p1/P2P2n1/1P1B2N1/2Q3PP/R4RK1 w - - -solution Bh7+ -id "ECM.264" -go -setboard r2rn1k1/1bqn1ppp/p7/2bpP1P1/1p1N1Q2/1P3B2/PBP1NR1P/3R2K1 w - - -solution Qxf7+ -id "ECM.265" -go -setboard r2r2k1/1bqpbpp1/ppn1p2p/2p1P1N1/P1B2B2/2P5/1PPRQPPP/3R2K1 w - - -solution Nxf7 -id "ECM.266" -go -setboard r1b1r1k1/1p1n1p2/p1nBp1p1/q2pP1Np/P7/R2B3Q/2P2PPP/5RK1 w - - -solution Nxf7 -id "ECM.267" -go -setboard r1b1k2r/ppp3pp/1qp5/2b1Pp2/3Qn3/5N2/PPPBNPPP/2KR3R w kq - -solution Qd8+ -id "ECM.268" -go -setboard 1k6/1p4p1/P1p5/5Q2/2q2r2/P7/8/4R2K w - - -solution Re8+ -id "ECM.269" -go -setboard 6k1/1p3p2/4r1p1/1PPrbq1p/2QN4/P2R1P1P/R4KP1/8 b - - -solution Bg3+ -id "ECM.270" -go -setboard 4n1q1/1k1b4/2p4R/2Pp4/1p1N1rpQ/3B4/PPP2P2/2K5 w - - -solution Nxc6 -id "ECM.271" -go -setboard r2qk2r/1pp2ppp/p3pnn1/6N1/1B1P2P1/1Q2P2P/PP3PK1/R6R b kq - -solution Nh4+ -id "ECM.272" -go -setboard 2kr2r1/R2p3p/2n2p2/1R3P2/2P1p3/4BQ2/1Pq4P/4K3 w - - -solution Ra8+ -id "ECM.273" -go -setboard r3r1k1/pp3pp1/2bb1q2/3p4/4n1Pp/2P1BB1P/PP2NPK1/R1Q4R b - - -solution Qxf3+ -id "ECM.274" -go -setboard r1br4/ppq2pk1/3bpN2/8/2P5/3n2B1/PP2QPPP/R4K1R w - - -solution Qg4+ -id "ECM.275" -go -setboard 6kb/B1p2p2/5r2/1r1p2pR/6q1/3P2P1/P1P2PP1/2KR3Q b - - -solution Rb1+ -id "ECM.276" -go -setboard rn4k1/pp2p1b1/4b3/q2p2Q1/2B2P2/8/P1P1K1P1/R6R w - - -solution Rh8+ -id "ECM.277" -go -setboard 3k2r1/pp1b3Q/1b2pP1p/3pN2q/5B2/7B/PP2nP1K/2R5 b - - -solution Rg2+ -id "ECM.278" -go -setboard 2r2rk1/pp3qpp/8/3P1b2/3Q4/P1N1B3/5PKP/R2R4 b - - -solution Rxc3 -id "ECM.279" -go -setboard r1b2rk1/1pqp1ppp/p1n1pn2/5N2/8/2PBB3/P1P2PPP/R2QR1K1 w - - -solution Nxg7 -id "ECM.280" -go -setboard 4k2r/1bqnbppp/p3p3/1p2P1N1/2r2BB1/8/PPP3PP/R2Q1R1K w k - -solution Nxf7 -id "ECM.281" -go -setboard 5r1k/1pqb3p/2n5/p1pB4/2Pp1r1P/1P5P/R2N2K1/3Q1R2 b - - -solution Bxh3+ -id "ECM.282" -go -setboard r4r2/pp2qk2/4pnp1/3pQ3/2p2P2/3B4/PPP2P2/2KR3R w - - -solution Bxg6+ -id "ECM.283" -go -setboard 3bq3/p1n2rk1/1pp1p1p1/3pPnPp/1P1P1N1P/2PN3K/P2BQR2/8 w - - -solution Nxg6 -id "ECM.284" -go -setboard b6r/2q2k2/4pPp1/1p1n2Bp/2pPB1PP/2P2Q2/8/4R1K1 w - - -solution Bxg6+ -id "ECM.285" -go -setboard r1b2r2/3pNpkp/3pn1p1/2pN3P/2PnP3/q3QP2/4BKP1/1R5R w - - -solution Qh6+ -id "ECM.286" -go -setboard 4k3/p4ppp/nb1P4/4p3/1P6/P4BP1/3q1r1P/2R2QKR w - - -solution Qb5+ -id "ECM.287" -go -setboard r2q1rk1/pp2pp2/3pb3/3Qb2R/4P1p1/1BN1Bn2/PPP2P2/2K4R w - - -solution Rh8+ -id "ECM.288" -go -setboard 2r1r1k1/pp1nbpp1/4pn1p/q3NN1P/P1pP1B2/2P5/1PQ2PP1/R3R1K1 w - - -solution Nxg7 -id "ECM.289" -go -setboard 1rbk2nr/p1pp2b1/7p/2qP1pp1/2Bn4/2NKBP2/PP2N1PP/R2Q3R b - - -solution Qxc4+ -id "ECM.290" -go -setboard 1r3rk1/6p1/p1pb1qPp/3p4/4nPR1/2N4Q/PPP4P/2K1BR2 b - - -solution Rxb2 -id "ECM.291" -go -setboard 5rk1/ppp1q2p/1n1p1np1/3P2N1/1P1R2P1/2Q4P/P3r1B1/5RK1 b - - -solution Rxg2+ -id "ECM.292" -go -setboard 6k1/pp2p2p/3p2p1/4b1r1/2Pn1r1q/2B1N3/PP4B1/R3QRK1 b - - -solution Qh2+ -id "ECM.293" -go -setboard r2qnr2/pp3kbQ/2npb1p1/2pN1pP1/4P3/8/PPP1BP2/R1B1K1NR w KQ - -solution Qxg6+ -id "ECM.294" -go -setboard r2k3r/2pb1ppp/p1p1q3/2Q5/5B2/2N5/PPP2PPP/3R2K1 w - - -solution Bxc7+ -id "ECM.295" -go -setboard 2r2r2/2N2pkp/2Q5/1N2p1b1/1p6/3P1qPb/PPR2P1P/4R1K1 b - - -solution e4 -id "ECM.296" -go -setboard 2r1qrk1/pp1b1ppp/4pn2/n1b5/8/2NQ1NP1/PP1BPPBP/R2R2K1 w - - -solution b4 -id "ECM.297" -go -setboard 2b1qr1k/7p/p1pR2p1/1pP1p1Q1/4P3/1B6/PP4PP/6K1 w - - -solution Re6 -id "ECM.298" -go -setboard 1r2k2r/2p2p1b/q1Pppb1N/p2p2P1/Q2P4/P3B3/1P5P/2KRR3 b k - -solution Rb4 -id "ECM.299" -go -setboard 8/2b2k1p/2N2pp1/P2p4/3KnP2/6BP/P5P1/8 w - - -solution Kxd5 -id "ECM.300" -go -setboard 4b1r1/1p3ppk/3r3p/p1p1qP1B/3pPN2/3P3R/nPPQ3P/6RK w - - -solution Ne6 -id "ECM.301" -go -setboard 4b2k/7p/3q3P/1p1pRpr1/1P1B4/2P2Q2/7K/8 w - - -solution Qg3 -id "ECM.302" -go -setboard 2kr2r1/pbp4p/1p4p1/1NqpNp2/4nQ2/8/PPPR1PPP/2K1R3 w - - -solution Nc6 -id "ECM.303" -go -setboard 1rr3k1/1p1bqpbp/1R1p2p1/pN1P4/P3P1n1/1Q6/3NB1PP/2B1R1K1 b - - -solution Rc3 -id "ECM.304" -go -setboard r1br4/1p3k2/p2q1p2/3N1Pp1/3Q4/8/PP4P1/4RR1K w - - -solution Re6 -id "ECM.305" -go -setboard r3kbnr/p4ppp/2p1p3/8/Q1B3b1/2N1B3/PP3PqP/R3K2R w KQkq - -solution Bd5 -id "ECM.306" -go -setboard 3r1rk1/2p1qp1p/pnP3p1/4n3/PP1NpR2/1BQ4P/6P1/5RK1 w - - -solution Ne6 -id "ECM.307" -go -setboard r1bq1k2/5pb1/p2p1n2/2pPrP2/2p4B/3B2R1/PP1Q2PP/R5K1 w - - -solution Qg5 -id "ECM.308" -go -setboard 2bq1rk1/p4pbp/p2p2p1/3P4/2p1QP2/2Pr3P/PP1N2P1/R1B2RK1 b - - -solution Bd4+ -id "ECM.309" -go -setboard r5rk/1p1bqpnp/3p1b1B/pBnPp2P/4P3/2N2Q2/PP3P2/R3KNR1 b Q - -solution Nf5 -id "ECM.310" -go -setboard r3rk2/p2q1b1p/1pnP1Qp1/5pN1/2p2P2/P1P5/7P/4RRK1 w - - -solution Re7 -id "ECM.311" -go -setboard r3r1k1/p1q2pp1/2p4p/7n/BPPP2N1/3Q1N2/P3n1P1/R1B4K b - - -solution Nhg3+ -id "ECM.312" -go -setboard r2qrbk1/1p4pb/2n2p1p/pNpn4/2N5/PP1PPQPB/1B5P/2R2RK1 w - - -solution Nbd6 -id "ECM.313" -go -setboard 5r1k/1p1b1p1p/p2ppb2/5P1B/1q6/1Pr3R1/2PQ2PP/5R1K w - - -solution Qh6 -id "ECM.314" -go -setboard r1b2rk1/p3bppp/2q5/8/2p1NR2/PnB1P3/1PB1Q1PP/3R2K1 w - - -solution Rd6 -id "ECM.315" -go -setboard r1rbb3/1R3pkp/2pBp1p1/p3P3/q6P/6P1/P2Q1P2/1R3BK1 w - - -solution R1b4 -id "ECM.316" -go -setboard 4r3/pp1b3k/2p2qpp/2Pp4/1P6/PBbP1N1P/2R3P1/3Q3K b - - -solution Re1+ -id "ECM.317" -go -setboard 1rb2rk1/p1p2ppp/2q5/3R4/2P1N3/bP4B1/P1Q2P1P/1K5R w - - -solution Bd6 -id "ECM.318" -go -setboard 8/5k1p/5PpB/3PR3/2r4P/1p3K2/2b5/8 b - - -solution Re4 -id "ECM.319" -go -setboard 4r2k/3b3p/p2p4/2pP1p2/q1B2Q1P/2PK1P2/Pr1R4/7R b - - -solution Re4 -id "ECM.320" -go -setboard rnb2rk1/pp2bppp/2p5/q7/4NN2/4B1QP/PPP3P1/2KR3R w - - -solution Rd5 -id "ECM.321" -go -setboard 2R5/2R4p/5p1k/6n1/8/1P2QPPq/r7/6K1 w - - -solution Rxh7+ -id "ECM.322" -go -setboard r1b1r1kb/p1qn1pnp/2p1p1p1/4P1N1/2p1NPP1/4B3/PPPQ3P/3RK2R w K - -solution Qxd7 -id "ECM.323" -go -setboard 5k1r/ppqnnp2/3b2p1/2pB2Qp/3p3N/1P1P2P1/P1P2P2/2B1R1K1 w - - -solution Rxe7 -id "ECM.324" -go -setboard 5rk1/5p1p/8/3pNp2/RPrqn3/1Q5P/1P3PP1/4R1K1 w - - -solution Rxe4 -id "ECM.325" -go -setboard 2rr2k1/4qppp/bn6/p1bB4/4N3/6P1/PB2PP1P/2RQ1RK1 w - - -solution Rxc5 -id "ECM.326" -go -setboard 3r1rk1/pbqn1pp1/1pp2n1p/2b1p1B1/P1B1P2N/2N4P/1PP1QPP1/3R1RK1 w - - -solution Rxd7 -id "ECM.327" -go -setboard 5rk1/r2nqpp1/p3p3/1p2P3/4N1pP/8/PPP3Q1/1K1R3R w - - -solution Rxd7 -id "ECM.328" -go -setboard 1rr3k1/4ppb1/2q1bnp1/1p2B1Q1/6P1/2p2P2/2P1B2R/2K4R w - - -solution Qh6 -id "ECM.329" -go -setboard 2r2r1k/1pq1b1p1/p1b1Qn1p/8/3B4/2NB4/PPP3PP/3R1R1K w - - -solution Rxf6 -id "ECM.330" -go -setboard 3rqb2/1pR3pk/p3n1pp/3p4/3B4/PP6/1P4PP/4RQK1 w - - -solution Rxe6 -id "ECM.331" -go -setboard rkb2qr1/1p5p/pQ1bp3/3p2B1/5n2/3B1N2/PP3PPP/2R2RK1 w - - -solution Rxc8+ -id "ECM.332" -go -setboard 2r2rk1/1bq2ppp/p7/1p1pNN2/1b1PnB2/3Q3P/PP3PP1/R3R1K1 w - - -solution Rxe4 -id "ECM.333" -go -setboard r3r1k1/1p3pPp/p1p5/3bb2N/7q/1P1Q4/2PB2PP/4RRK1 w - - -solution Rxe5 -id "ECM.334" -go -setboard 8/5p2/4p1k1/4P1p1/2rBRbK1/5P2/r4P2/7R b - - -solution Rxd4 -id "ECM.335" -go -setboard r1bqr1k1/pp3pp1/2p4p/3PN1n1/3P1b2/2NB4/PPQ2PPP/3R1RK1 b - - -solution Rxe5 -id "ECM.336" -go -setboard r3r1k1/pbqn1ppp/1p3n2/1Pb5/2p5/5N1P/PBQ1BPP1/1N1R1RK1 b - - -solution Rxe2 -id "ECM.337" -go -setboard 2r2rbk/6bp/5qp1/1N1p1n2/4p3/1p5P/1P3PP1/RBBQ1RK1 b - - -solution Ng3 -id "ECM.338" -go -setboard r1b1r1k1/pp3p1p/1q2p1pQ/2b1P1B1/8/P2B3P/1P3PP1/2R1R1K1 w - - -solution Rxc5 -id "ECM.339" -go -setboard 1k2r2r/1bpQN2p/1p6/P3p2q/4np2/2P2N2/2P2PPP/R3R1K1 b - - -solution Rxe7 -id "ECM.340" -go -setboard r1k2n1r/2pb4/1p1p1qpp/p2P1p2/3N1P1P/2Q3P1/PPP1R3/2K1RB2 w - - -solution Re8+ -id "ECM.341" -go -setboard r1b2r1R/1p2bpk1/4p1p1/4P1N1/p2p4/5Q2/qPP2PP1/1NKR4 w - - -solution Qf6+ -id "ECM.342" -go -setboard rnb2rk1/3n1ppp/p3p3/1p2P1q1/3N4/1BN5/PPP3PP/R2Q1RK1 w - - -solution Nxe6 -id "ECM.343" -go -setboard 4q1r1/5kpp/2p1nb2/2PpQp2/r4P2/1N3P2/1BP1R2P/6RK w - - -solution Rxg7+ -id "ECM.344" -go -setboard 2r1k2r/1pq1bppp/p3bn2/4N3/2pPN3/2P5/P3QPPP/R1B1R1K1 w k - -solution Nxf6+ -id "ECM.345" -go -setboard 1r3k1r/4Rn1p/pb1p1P2/1p1q2P1/5p2/7Q/PPP1B1RP/2K5 w - - -solution Rxf7+ -id "ECM.346" -go -setboard 4rrk1/ppn3pp/2pb1n1q/3p4/1P1P4/P3BNP1/2P1BPKP/R2Q1R2 b - - -solution Rxe3 -id "ECM.347" -go -setboard 2b1r2k/2Q4p/4q3/1pp5/3b4/4NP2/1P3BP1/3R2K1 w - - -solution Rxd4 -id "ECM.348" -go -setboard r3r1k1/pp3ppp/1qp5/2bbNQ2/5B2/2P5/PP3PPP/3RR1K1 w - - -solution Rxd5 -id "ECM.349" -go -setboard 7r/3qbk1r/3p1n2/p1pPp1p1/Pp1nPpP1/1P1Q1P1B/1NP3K1/R3B2R b - - -solution Nxg4 -id "ECM.350" -go -setboard r2qn1k1/1p1b3p/2pp1b2/6pQ/p1P1P3/P1N3PB/1P6/3R1R1K w - - -solution Rxf6 -id "ECM.351" -go -setboard 2rq3r/5p2/p3pkbQ/3p4/3N4/2P4R/P4PP1/4R1K1 w - - -solution Rf3+ -id "ECM.352" -go -setboard r4k1r/pp2pp2/3p1b2/q2PnQpp/3N4/1BP3P1/PP3PP1/3RR1K1 w - - -solution Rxe5 -id "ECM.353" -go -setboard 5rk1/2p3pp/p1p4r/2P1p3/3pPnq1/1P3R1P/PN1Q3K/R5N1 b - - -solution Qxf3 -id "ECM.354" -go -setboard r1b3rk/pp1n3p/2p2b1n/4pp1q/2P5/1P3NPP/PBQ1NPB1/3RR1K1 w - - -solution Rxd7 -id "ECM.355" -go -setboard 2r2rk1/p3p1bp/3qP1p1/1Q1p4/P4P2/3R4/1P4PP/2B2RK1 b - - -solution Rxc1 -id "ECM.356" -go -setboard r3r1k1/4pp1p/b5pB/q1pP4/3b4/2N4P/PP4P1/R2RQ2K w - - -solution Rxd4 -id "ECM.357" -go -setboard 1r6/3b1pk1/pr1p1q1p/2pPb1p1/2B1P1P1/2N2PP1/1PQ3K1/RR6 b - - -solution Rxb2 -id "ECM.358" -go -setboard 2r2rk1/5ppp/1p2p3/p7/1n1R4/P1NnPN2/1P3PPP/1R4K1 b - - -solution Nxb2 -id "ECM.359" -go -setboard 2r3k1/p1r1ppb1/2bp1np1/q5N1/1p1BP2Q/3P4/PP2N1PP/2R2R1K w - - -solution Bxf6 -id "ECM.360" -go -setboard r3r1k1/p4ppp/2pp1b2/8/q2n1P2/2BQ3P/PPP3P1/1K1R2NR b - - -solution Re3 -id "ECM.361" -go -setboard rn1kr3/pppb4/3p1q2/3P1pNn/2P2P1p/1P2Q2P/P2NR1B1/4R1K1 w - - -solution Qxe8+ -id "ECM.362" -go -setboard r2q1rk1/ppp1b1pp/1nn1p3/4P3/2PP4/2N1B2P/PP1Q4/2KR1B1R b - - -solution Rxf1 -id "ECM.363" -go -setboard r3k3/1pqb1p2/p3p3/4n2r/3N1Q2/2P5/PP4B1/4RRK1 w q - -solution Rxe5 -id "ECM.364" -go -setboard 1rb2bk1/q4r2/pB1p1nn1/3Pp3/N3PpP1/8/1PQ1BNP1/R1R3K1 b - - -solution Rxb6 -id "ECM.365" -go -setboard 2rbr2k/1pq2ppp/p3bn2/4p1B1/P3P3/2N3Q1/1PP1B1PP/3R1R1K w - - -solution Rxd8 -id "ECM.366" -go -setboard r4rk1/5p2/p1b1pQpq/8/1B2P3/2NR4/PPP3PP/1K6 w - - -solution Rd5 -id "ECM.367" -go -setboard 3nr1k1/p6p/2n1pRpB/3pP3/2pP2P1/q1P4P/6BK/5Q2 w - - -solution Rxg6+ -id "ECM.368" -go -setboard 2rr2k1/5ppp/p1b1pn2/q1p5/P1B1PQ2/R1PN1P2/6PP/1R4K1 b - - -solution Rxd3 -id "ECM.369" -go -setboard 1r2r1k1/1pqb2pp/p2bp1n1/2p3BQ/3pN3/1P1P2P1/P1P3BP/4RRK1 w - - -solution Bd8 -id "ECM.370" -go -setboard 1k2rb1r/pbp1q1pp/2pp4/2PnpP2/Q7/1P3NP1/PB5P/2KR1B1R w - - -solution Rxd5 -id "ECM.371" -go -setboard 7k/p1p1r2p/1p1pnr1q/5pp1/2PPPp2/2PB1P1P/P2Q1PRK/7R b - - -solution g4 -id "ECM.372" -go -setboard 5qk1/p4p2/1p3Pp1/2p4Q/3rr3/7R/PP6/6RK b - - -solution Rh4 -id "ECM.373" -go -setboard 5r1k/5Bb1/3p2Pp/p1pP4/1p2Q3/4P1P1/q7/3K3R w - - -solution Rxh6+ -id "ECM.374" -go -setboard 2b4B/1pp2k2/1p6/3P3n/5p1P/1P4p1/P2r2P1/R5KR b - - -solution f3 -id "ECM.375" -go -setboard r3k2N/pppbq1pp/5n2/3Ppn2/2BP4/2P3bP/PP2Q1P1/RNB2K1R b q - -solution Bh2 -id "ECM.376" -go -setboard r2q1r2/p1pbppbk/1p3npp/n2P2B1/7Q/2NB1N2/PPP3PP/4RRK1 w - - -solution d6 -id "ECM.377" -go -setboard 5n2/3bp1r1/1r1p3k/p1p2pNp/1nP2P1P/1PN1PB1K/P5R1/6R1 w - - -solution Nf7+ -id "ECM.378" -go -setboard r3bb2/P1q3k1/Q2p3p/2pPp1pP/2B1P3/2B5/6P1/R5K1 w - - -solution Bxe5+ -id "ECM.379" -go -setboard r1b1qbk1/7p/4p1p1/1p2Bp2/p1B2Q2/P3P3/1P3PPP/3R2K1 w - - -solution Bc3 -id "ECM.380" -go -setboard 2r1k2r/3b1pb1/p2ppp2/2q2P2/2p1P2p/P1N2N2/1PP1Q1PP/3R1R1K w k - -solution e5 -id "ECM.381" -go -setboard 3r1rk1/1pq1bppp/p7/n2RN3/P7/1P6/1B2QPPP/R5K1 w - - -solution Nd7 -id "ECM.382" -go -setboard q1r3k1/2r4p/2p3pP/2Qp1p2/PRn5/4P1P1/5PB1/1R4K1 w - - -solution Rxc4 -id "ECM.383" -go -setboard r3bknr/1p4pp/p3Q3/q4pb1/P1Bn4/8/1PP2PPP/R1B1R1K1 w - - -solution Qd6+ -id "ECM.384" -go -setboard r1b1nrk1/p2n3p/2pp4/8/2P2q1N/1PB4P/P4PB1/R2QR1K1 w - - -solution Bd5+ -id "ECM.385" -go -setboard 7k/5rp1/p1bQB2p/1p4nN/4P3/1Pq5/P5PP/3R3K b - - -solution Bxe4 -id "ECM.386" -go -setboard r4r1k/1bq4p/p4bp1/3pn3/NP6/Q1PBB2P/6P1/3R1RK1 b - - -solution d4 -id "ECM.387" -go -setboard r2r2k1/1bq2pb1/6pp/2P1p3/1nBpN2N/6PP/2Q2P2/3RR1K1 w - - -solution Nf6+ -id "ECM.388" -go -setboard r3rbk1/p1q2pp1/1n5p/2pp4/4N3/1PQ3P1/PB2PP1P/3R1RK1 w - - -solution Nf6+ -id "ECM.389" -go -setboard 2k1r3/Qp3pb1/2p4p/6q1/8/1P4PN/PBPn1P2/K2R4 b - - -solution Qe5 -id "ECM.390" -go -setboard r3r1k1/1pp2p1b/7p/2Q5/2PP2p1/2P1NpPq/P2R1P1P/6RK b - - -solution Rxe3 -id "ECM.391" -go -setboard 5bk1/5p1p/Q5p1/1B4n1/8/2q1p3/6PP/3R2K1 b - - -solution e2 -id "ECM.392" -go -setboard r5k1/1p3pp1/1p3q1p/1P1bQ3/P2P3n/8/3B1PPP/R3R1K1 b - - -solution Nf3+ -id "ECM.393" -go -setboard 2k2r1r/1pp1nqbp/p2p1p2/5P2/2PBN3/1P3Q2/1P4PP/R4RK1 w - - -solution Rxa6 -id "ECM.394" -go -setboard r5kr/p3PR1p/1p1p2pq/4n3/8/3B4/P4QPP/4R1K1 w - - -solution Rxe5 -id "ECM.395" -go -setboard 2bq1b2/1p3k2/1r1p1p1p/pNpP1PpP/P1P3P1/6B1/1P5Q/2K1R3 w - - -solution Re6 -id "ECM.396" -go -setboard 2kr3r/pp3p1p/2b1p3/q3Q3/5P2/2P1B3/P1P3PP/1R3RK1 b - - -solution Rhg8 -id "ECM.397" -go -setboard 2br1rk1/2qnQ2p/p4pp1/4p3/P1p1P1N1/2Pn1N1P/2B2PP1/1R1R1K2 w - - -solution Rxd3 -id "ECM.398" -go -setboard 2r2nk1/6pp/p1rPqp2/1pP1p3/1P6/2B3R1/1P2Q1PP/5RK1 w - - -solution Rxf6 -id "ECM.399" -go -setboard 3r2k1/pb3p1p/1q4p1/2b1P3/1Pp5/2P1p1P1/P3Q1BP/R1B4K b - - -solution Qc6 -id "ECM.400" -go -setboard r2rnbk1/pp3ppp/1q4n1/3p1N2/5P2/1PQB4/PBP3PP/4RR1K w - - -solution Nh6+ -id "ECM.401" -go -setboard r3nrk1/p1pb1qpp/3p1p2/2pP1N2/2P1PR2/P1PB4/4Q1PP/5RK1 w - - -solution e5 -id "ECM.402" -go -setboard r1b2rk1/pp2qp2/2nRp1p1/6Pn/1Pp5/P1N1P3/1BQ2PP1/2K2B1R w - - -solution Rxh5 -id "ECM.403" -go -setboard 5rr1/pp1kq1bp/2bppn2/2p3B1/4N3/2NP2P1/PPP4P/R3QRK1 b - - -solution Nxe4 -id "ECM.404" -go -setboard 1r1q1k2/R4pp1/5p1r/1p1p1n1p/3Pb2N/1BP5/2P1Q1PP/4R1K1 w - - -solution Qxe4 -id "ECM.405" -go -setboard R2b2k1/2rq1n1p/3p2p1/1p1Ppp1n/1P6/1B1PQN1P/1B3PP1/6K1 w - - -solution Nxe5 -id "ECM.406" -go -setboard r1b3kr/bp3ppp/p1nNp3/4P3/P1Q2P2/BBP5/5qPP/R2R3K w - - -solution Nxc8 -id "ECM.407" -go -setboard 6k1/5rb1/6pp/1p2p3/2b5/4B1P1/r4PBP/1RR3K1 w - - -solution Rxc4 -id "ECM.408" -go -setboard 4r1k1/1p5p/5ppP/1qnP4/r4R2/P6P/1B1Q2K1/5R2 w - - -solution Rxf6 -id "ECM.409" -go -setboard r2q1rnk/1p1bb1pp/p2p4/3NP1P1/P2Bp3/3Q3R/1PP1B2P/6RK w - - -solution e6 -id "ECM.410" -go -setboard r4rk1/1b4bp/p1npq1P1/R3p3/8/1N2BP2/1PPQ2P1/4KB1R w K - -solution Qxd6 -id "ECM.411" -go -setboard 6k1/pbq2pp1/4r2p/8/3prb2/1P1Q3P/PN2BPP1/3RRK2 b - - -solution Bd2 -id "ECM.412" -go -setboard 6k1/2p3p1/2b4p/p4P2/2pR2PK/2P1r2P/1B6/8 b - - -solution Bg2 -id "ECM.413" -go -setboard r1b2rk1/pp2ppbp/2np1np1/q7/4P3/2N1BN2/PPPQBPPP/2KR3R b - - -solution Nxe4 -id "ECM.414" -go -setboard 3r1rk1/p1q4p/1p3bp1/3bnp2/3B4/1NPB3P/P1PQ2P1/1R2R2K b - - -solution Nf3 -id "ECM.415" -go -setboard r2q1rk1/p5pp/bn1p1p2/2pP4/1bP1NP2/3B1R2/PBQ3PP/R5K1 w - - -solution Ng5 -id "ECM.416" -go -setboard 1r1q2k1/7p/2p3p1/1P1n1b1r/2BQ4/4B3/P6P/1R3RK1 w - - -solution bxc6 -id "ECM.417" -go -setboard r3kbnr/1ppb3p/p1q1pp2/3p4/3P4/2N2N2/PPP2PPP/R1BQ1RK1 w kq - -solution Ne5 -id "ECM.418" -go -setboard r1b1qbk1/pp3r1p/2pR1np1/5p2/2P1pP2/1PN1Q1PP/PB2P1B1/5RK1 w - - -solution Rxf6 -id "ECM.419" -go -setboard 3rr1k1/1p1qb2p/pP4p1/2p2p2/4RQ2/3P4/1PPB2PP/4R1K1 w - - -solution Bc3 -id "ECM.420" -go -setboard 2b2r2/rpp2pkp/3pq3/2pR2P1/2n1P1N1/2P2B2/1P3Q1P/1K5R b - - -solution Qxd5 -id "ECM.421" -go -setboard 6k1/rnqb3p/5ppQ/2pPp3/p1N1P3/2PB3P/5PP1/1R4K1 w - - -solution f4 -id "ECM.422" -go -setboard 7r/pp4k1/3pn1p1/q1pB1b2/2P5/5NR1/1P3PP1/2Q3K1 w - - -solution b4 -id "ECM.423" -go -setboard 5r2/4prkp/3q2p1/2pP1n2/N1Q2R2/1P4PP/P5K1/4R3 b - - -solution g5 -id "ECM.424" -go -setboard B1b1rbk1/5ppp/pq6/1p2n3/2p2P2/6PP/PPQ4K/R1BR4 b - - -solution Ng4+ -id "ECM.425" -go -setboard 8/2R4p/2R3bk/8/5N1P/1p3PP1/2pqP1K1/8 w - - -solution g4 -id "ECM.426" -go -setboard 6k1/p4p1p/3pqp2/2p1r3/2Pnr1N1/P6P/1P1Q1PP1/3R1RK1 b - - -solution Ne2+ -id "ECM.427" -go -setboard 1kqr3r/p1p4p/RpQP4/2P1p3/4B3/4Bp2/P4P1P/6K1 b - - -solution Rdg8+ -id "ECM.428" -go -setboard 6k1/3Q1pp1/4p2p/p2pP3/1p1P2P1/nP1R1N2/q1r2PKP/8 w - - -solution Qe8+ -id "ECM.429" -go -setboard 6k1/5pp1/p5r1/3Np2q/4P2p/1r3P1Q/n4P1P/3R2RK w - - -solution Rxg6 -id "ECM.430" -go -setboard nr2rb2/3q1kpp/3p4/pnpPpNBP/2N1P3/3Q4/PP3P2/2K3RR w - - -solution Nh6+ -id "ECM.431" -go -setboard r3k1r1/1pp2ppp/pb1p1qn1/4p2b/2B1P3/N1PP1NPP/PP2QPK1/R4R2 b q - -solution Nh4+ -id "ECM.432" -go -setboard 1n2n1r1/r3qppk/p2p3p/3PpP1P/1P2P3/3B2RQ/3B1P1K/6R1 w - - -solution Bxh6 -id "ECM.433" -go -setboard r2b4/pb2q1k1/np1p2r1/1N1Pp2Q/PPN1Pp2/8/2R2BPp/2R4K w - - -solution Ncxd6 -id "ECM.434" -go -setboard r7/2qnb1kp/p2p1nP1/1p1Pp1p1/6N1/3BB2Q/PPP4P/5RK1 w - - -solution Qxh7+ -id "ECM.435" -go -setboard n1rqk3/1p2p3/1n1pP1p1/pP1P1p1r/3B3P/1BN3p1/3Q4/R3K2R w KQ - -solution Qg5 -id "ECM.436" -go -setboard rn2kr2/pp1b1pQp/3Pp3/qB1n4/3N4/P7/1PP2PPP/2KR3R w q - -solution Nxe6 -id "ECM.437" -go -setboard 3r1r2/1b4bk/p1n3pp/1p2p3/4P3/q1P1QNB1/B4RPP/5RK1 w - - -solution Bh4 -id "ECM.438" -go -setboard r1r3kb/1pqbpp2/p2p1npB/n7/3NP3/1BN2P2/PPPQ2P1/2KR3R w - - -solution Bf8 -id "ECM.439" -go -setboard 4rr1k/pbp3bp/3p4/1p5q/3n4/1B1Q2B1/PP1N1N1P/4RRK1 b - - -solution Rf3 -id "ECM.440" -go -setboard r3k2r/pp2ppbp/1qp5/2n2b2/2P5/4BN2/PP1QBP1P/2KR3R b kq - -solution Nb3+ -id "ECM.441" -go -setboard 4qn1k/1b3rpp/pb2pB2/1pp1P2P/3p1NQ1/P2P2PB/1PP4K/5R2 w - - -solution Ng6+ -id "ECM.442" -go -setboard 2b5/2qrrpk1/5Rp1/2p4p/1pB1PR1P/1P1P2P1/5Q1K/8 w - - -solution Qb2 -id "ECM.443" -go -setboard 2r3k1/n3qppp/1B1r1n2/N7/N4P2/1Q4PP/P5B1/R1b4K b - - -solution Qe1+ -id "ECM.444" -go -setboard 7r/2p1q1k1/1p3r1p/p1pPpPp1/2P1P1P1/PP6/3Q2KR/7R w - - -solution Qxg5+ -id "ECM.445" -go -setboard rnb2k2/ppbp2p1/2p2pB1/8/1PN5/2P5/P4PPP/R1B3K1 w - - -solution Bf4 -id "ECM.446" -go -setboard 2k5/1p1b4/2n1p1r1/7q/4RP2/3BQN2/2P2K1P/8 w - - -solution f5 -id "ECM.447" -go -setboard 2k3r1/1ppr3p/p1pB2q1/6n1/N3Pp2/8/PPP1Q1PP/3R1RK1 b - - -solution f3 -id "ECM.448" -go -setboard r3q2r/2p1k1p1/p5p1/1p2Nb2/1P2nB2/P7/2PNQbPP/R2R3K b - - -solution Rxh2+ -id "ECM.449" -go -setboard 6r1/6rk/3p3p/p2Rp3/R1PqPp1b/2N2Q2/6PP/7K b - - -solution Rxg2 -id "ECM.450" -go -setboard 4r1k1/1p2rq1p/2bQpp2/p1Pp4/3P4/P3RN2/5PPP/4R1K1 w - - -solution Ne5 -id "ECM.451" -go -setboard b2b1r1k/3R1ppp/4qP2/4p1PQ/4P3/5B2/4N1KP/8 w - - -solution g6 -id "ECM.452" -go -setboard 1k4q1/1p2Rprp/p1p5/2Pp4/1P1Q3P/6P1/P5K1/8 w - - -solution Qe5+ -id "ECM.453" -go -setboard r2r3k/ppq3pp/2p1n3/4NQ2/3P4/1B6/PP3PPP/6K1 w - - -solution Ng6+ -id "ECM.454" -go -setboard r4r1k/p2bN2p/1p1p1p2/2q4P/3Q4/1P6/1PP3P1/1K2R2R w - - -solution Ng6+ -id "ECM.455" -go -setboard 2kr3r/p5b1/Pp2R1p1/2q2p1p/2p2Pn1/2P2BP1/1PQB2P1/5R1K b - - -solution h4 -id "ECM.456" -go -setboard r1b2k1r/1p2qp2/pN1p1n2/2pP2pp/P2bP1P1/2N5/1P2BPP1/R2Q1RK1 b - - -solution hxg4 -id "ECM.457" -go -setboard 6nk/p4rrp/1p1p1p2/1q1Pp2R/4B1P1/1PP2Q1R/P6P/7K w - - -solution g5 -id "ECM.458" -go -setboard r1b1k2r/6pp/p1p1p3/3np1B1/1b2N3/8/q1PQB1PP/3RK2R w Kkq - -solution Qxb4 -id "ECM.459" -go -setboard 3qrbk1/1r3p2/p1bp1Pp1/1p2p1Pp/4P2R/1P2BB1Q/P1P4P/R6K w - - -solution Bxh5 -id "ECM.460" -go -setboard 2r3k1/4qpp1/2p1r3/pbQpN1Pp/3P1P1P/4P3/PP6/1KR3R1 w - - -solution Qxb5 -id "ECM.461" -go -setboard 4r2k/p2Q3p/2p1r1p1/4NpR1/3P4/2P1PP1b/P4q1P/6RK w - - -solution Nxg6+ -id "ECM.462" -go -setboard 4r1kb/p2r4/1np1p1p1/3nP1Bp/1p3P1R/1B2qP2/PPPN3Q/2K4R w - - -solution Rxh5 -id "ECM.463" -go -setboard 2rq2k1/pp1bpp1p/3p1np1/8/3NPPnQ/1BP5/P1P3P1/1K1R3R w - - -solution e5 -id "ECM.464" -go -setboard r1b3k1/3nqp1p/p1n1p1pP/3pP1N1/Pp3QN1/1Pr3P1/2PR1P2/4RBK1 w - - -solution Rxd5 -id "ECM.465" -go -setboard 2r1kr2/p1qbbp1Q/3pp3/5p2/1p1N1PB1/6P1/PPP4P/K2RR3 w - - -solution Bxf5 -id "ECM.466" -go -setboard r6k/ppr1qpp1/4n2p/3pP3/3P2R1/3BQ2P/P5PK/5R2 w - - -solution Rf6 -id "ECM.467" -go -setboard 2rr2k1/5pn1/1b2q1pp/pP1pP3/2pP1PP1/2R1B2P/2Q3B1/3R3K w - - -solution f5 -id "ECM.468" -go -setboard 2Q2bkr/5p2/1rb5/1pN1n1qB/1P1pPN2/6P1/5P2/R1R3K1 w - - -solution Nce6 -id "ECM.469" -go -setboard 2r4r/1q1kb1p1/4p2p/3pP3/np6/3RBP2/NPP2Q1P/1K2R3 b - - -solution Nc3+ -id "ECM.470" -go -setboard 2r4r/3bk1b1/1q2pp2/3pPp1p/1p1N1P1P/1P1R1BP1/P2Q4/1K1R4 w - - -solution Nxf5+ -id "ECM.471" -go -setboard 1rb3k1/2r2pbp/p2q1npn/P1pPp1N1/2B1P3/2P1B1NP/5QP1/3R1RK1 w - - -solution Ne6 -id "ECM.472" -go -setboard r7/pp4pk/2n3qp/5p2/3p4/P4QB1/1PP1RPPP/6K1 b - - -solution d3 -id "ECM.473" -go -setboard 2r2r1k/1p3p1p/p2p1Pp1/3Pp3/1q2Bn1Q/8/1PP3PP/4RR1K w - - -solution Rxf4 -id "ECM.474" -go -setboard r4rk1/5ppp/p7/4PR2/P1qnp2Q/4B3/1P4PP/5RK1 w - - -solution Rh5 -id "ECM.475" -go -setboard 4r3/pb3pk1/6p1/8/6P1/1PNrB2K/P4P1P/2R5 b - - -solution Rexe3+ -id "ECM.476" -go -setboard 1r3kr1/p3q1b1/3p2Q1/2p2p2/2P2N2/4Pn2/PB6/K2R3R w - - -solution Ne6+ -id "ECM.477" -go -setboard 5r1k/6bp/3p1q2/3Q1nP1/8/6P1/PP3BKP/R2N4 b - - -solution Ne3+ -id "ECM.478" -go -setboard 1r1q1k1r/Q4ppp/2B2n2/8/Pbbp4/1P4P1/2PP1P1P/R1B1K2R b KQ - -solution Qe8+ -id "ECM.479" -go -setboard 5rk1/p1p1R1p1/1p5p/8/2pP1P2/3n2P1/PP2Q2P/5KNq b - - -solution Nxf4 -id "ECM.480" -go -setboard r5k1/1N3ppp/4b3/1Nq5/Pn3pn1/5B2/1PPQ3P/R6K b - - -solution Qxb5 -id "ECM.481" -go -setboard 1r2k2r/1bq2pp1/pn2p2p/1p2P1b1/3N4/2N3Q1/PPP3PP/1K1R1BR1 w k - -solution Bxb5+ -id "ECM.482" -go -setboard r1bqn1r1/p2k1pp1/1p5p/n1pPpN2/2P1P3/P1PBB2P/4Q1P1/R4RK1 w - - -solution Nxh6 -id "ECM.483" -go -setboard 2r2r2/p4p1k/1p2pBpn/2p5/6N1/8/P4P2/R3R1K1 w - - -solution Kg2 -id "ECM.484" -go -setboard r4rk1/p1p2pp1/bp1p1q1p/n1nPpN2/2P1P2P/2PB4/P2BQPP1/R3K2R w KQ - -solution Bg5 -id "ECM.485" -go -setboard 3rn1k1/pp3ppp/6b1/2p1Q3/2P1P3/P4P2/1B1qBP1P/1R5K w - - -solution Rd1 -id "ECM.486" -go -setboard 4r1k1/5ppp/p1q3b1/1ppNr3/2P1P3/1P4QP/P4PP1/4RRK1 w - - -solution f4 -id "ECM.487" -go -setboard 2kr2r1/2pqbp1p/p1n1b3/1P1pP3/4n3/1BP1BN2/1P4PP/RN1Q1RK1 b - - -solution Bh3 -id "ECM.488" -go -setboard 2b3k1/5qp1/p2r1n1p/3p4/3B4/1RP3QP/P2Nr1P1/3R2K1 b - - -solution Ne4 -id "ECM.489" -go -setboard 6k1/5rp1/4Nb1p/2p5/4Q3/1r5P/q5P1/3R1R1K w - - -solution Rxf6 -id "ECM.490" -go -setboard 1r2r1k1/p4ppb/q1pbnn1p/4p3/N1P1P3/pPB2NP1/2Q1RPBP/R5K1 b - - -solution Nxe4 -id "ECM.491" -go -setboard r4r2/1pnqb2k/3p1p1p/p1pPpPpP/2P1N1P1/4BP2/PP1Q4/R2R2K1 w - - -solution Bxc5 -id "ECM.492" -go -setboard rnb3kr/ppp2ppp/1b6/3q4/3pN3/Q4N2/PPP2KPP/R1B1R3 w - - -solution Nf6+ -id "ECM.493" -go -setboard 6kr/1p1r1p1p/p2pnPpQ/3Np2R/2q1P3/5P2/PPP5/2KR4 w - - -solution Rxe5 -id "ECM.494" -go -setboard 3Rq1k1/1pp1Ppbp/r1b3p1/p7/P1N5/1P4P1/1B2PP1P/2R3K1 w - - -solution Nd6 -id "ECM.495" -go -setboard r2q1rk1/pp1n2pp/2pb4/1P1p3n/P1PPpP2/4P2b/1BNNBP1P/R2QR1K1 b - - -solution Nxf4 -id "ECM.496" -go -setboard rq1r2k1/3nbp1p/p2p2p1/1p1Pp1P1/3B1P2/3B3Q/PPP4P/2KR2R1 w - - -solution f5 -id "ECM.497" -go -setboard r4rk1/1bq1bppp/8/p2pPR2/3B4/1NnP3Q/1P4PP/4R1K1 w - - -solution Rh5 -id "ECM.498" -go -setboard r4rk1/1nqb1pbn/6pB/pppPp2p/8/1PP2NNP/P1BQ2P1/R4RK1 w - - -solution Nxh5 -id "ECM.499" -go -setboard r1b2rk1/pp4bp/2p3p1/4q2n/NQPN1p1P/1P5n/P3PPB1/2BRRK2 b - - -solution f3 -id "ECM.500" -go -setboard 4rqk1/p5b1/3p3p/3Pn1p1/1pP1NrP1/4NP2/P3Q1P1/1K1RR3 b - - -solution Nxf3 -id "ECM.501" -go -setboard r2qkr2/1p3pQp/2p1b3/1B1pN3/1p6/8/1P1K1PPP/2B1R3 w q - -solution Nxc6 -id "ECM.502" -go -setboard r3rk2/pb2bpp1/1n5p/q1pP1B2/1p3B2/5N2/PPQ2PPP/R2R2K1 w - - -solution Bc8 -id "ECM.503" -go -setboard r1bqr2k/ppp2p1p/5b2/3Pn1p1/1PP1Bp2/8/PB2N1PP/R2Q1RK1 w - - -solution Nxf4 -id "ECM.504" -go -setboard 1q4k1/p3rppp/3b1n2/8/5P2/4P3/PP1B3P/RNQ3K1 b - - -solution Bxf4 -id "ECM.505" -go -setboard r3k2r/1p1b1p2/p3p3/2b3p1/4N1n1/1B5P/PPP3P1/R1B2R1K b kq - -solution Bc6 -id "ECM.506" -go -setboard r3kb1r/npqbpp1p/p3n1p1/P3P3/6N1/3B1NB1/1PP3PP/R3QRK1 w kq - -solution Ng5 -id "ECM.507" -go -setboard 7k/p6p/4bppb/1N6/P2p1p2/1PrP2P1/2r1P2P/1R1Q2K1 b - - -solution f3 -id "ECM.508" -go -setboard 2kr4/pp1r1pp1/4n1p1/4R3/2Pp1qP1/3P2QP/5PB1/1R4K1 w - - -solution Rc5+ -id "ECM.509" -go -setboard 3r1qk1/5p1p/4b1pP/1pRp4/1P1Q4/r5P1/5P2/4RBK1 w - - -solution Rxe6 -id "ECM.510" -go -setboard r2qkn1r/pb2bp2/1pp1p3/5p1p/2BPN3/5NQ1/PPP2PPP/1K1RR3 w kq - -solution d5 -id "ECM.511" -go -setboard 2rr1nk1/pp2q1b1/4p1p1/5p2/1nB5/2B1N1QP/PP4P1/4RRK1 w - - -solution Nxf5 -id "ECM.512" -go -setboard 3r3k/p4npp/1p2qpb1/4P3/5P2/P3N3/1B2QP1P/6RK w - - -solution exf6 -id "ECM.513" -go -setboard 6rk/1b2bp1p/ppq1p3/2p1Pp2/P1Br1P2/1PNR2P1/2P1QK1P/6R1 b - - -solution Rxf4+ -id "ECM.514" -go -setboard r3r1k1/p1q2pp1/b1pb3p/n7/B3n3/2P1BN2/PP1N1PPP/R2QK2R b KQ - -solution Nxc3 -id "ECM.515" -go -setboard 4qrk1/pp1bbppp/4pn2/2r1Q1B1/7P/2N3R1/PPP2PP1/2KR1B2 w - - -solution Bxf6 -id "ECM.516" -go -setboard r5n1/pppb1B2/2k1pb2/3p2B1/5P2/2N5/PPP3P1/2KR4 w - - -solution Nxd5 -id "ECM.517" -go -setboard 2b2q2/r2n2kp/1n1p1pp1/p1pP4/1pP1NPN1/1P1B4/P5PP/Q3R1K1 w - - -solution Nexf6 -id "ECM.518" -go -setboard 1k1r2r1/p1p3q1/2p2p2/3pn3/1P1b2bB/1QN5/P4PPP/1R2RBK1 b - - -solution Nf3+ -id "ECM.519" -go -setboard r3k2r/1bq1bp1p/p2p1np1/1p2p3/3NP3/P1N2QB1/1PP2PPP/3RR1K1 w kq - -solution Ndxb5 -id "ECM.520" -go -setboard r2q4/5p1P/2p1bR2/ppk1p1P1/3pB2Q/8/PPP5/6K1 w - - -solution g6 -id "ECM.521" -go -setboard r3kb1r/pp1b1pp1/4p3/2q3Qp/5B2/2PB4/P4PPP/R3R1K1 w kq - -solution Rxe6+ -id "ECM.522" -go -setboard 1rbr2k1/p4pp1/5b1p/2p1p3/3qPP2/1PNB2PP/PKP2Q2/5R1R b - - -solution c4 -id "ECM.523" -go -setboard r3r1k1/p6p/bp4p1/2bPN3/6n1/6P1/PB2RPB1/3R2K1 w - - -solution d6 -id "ECM.524" -go -setboard 2bk3r/3r2pp/p2nQ3/q2R2P1/1p6/8/PPP2P1P/1K3B1R b - - -solution Qxd5 -id "ECM.525" -go -setboard 3r1k2/1q1r1pp1/3p2bp/p2N4/8/2P1RQ1P/P4PP1/4R1K1 w - - -solution Nf6 -id "ECM.526" -go -setboard 3q3r/p2r1pkp/bp1N1np1/n2p4/5Q2/B1P5/P4PPP/R3R1K1 w - - -solution Nf5+ -id "ECM.527" -go -setboard r4Nk1/1p2pp1p/p1np2p1/3N4/P3PPn1/8/1bPKBqPP/R2Q1R2 b - - -solution Qd4+ -id "ECM.528" -go -setboard 1kb3rr/1p2n2q/1Np2p2/1p6/1P1N1P1p/8/2P1RQPP/R6K w - - -solution Nxb5 -id "ECM.529" -go -setboard r3r1k1/p3Rp1p/1qp1bQ2/8/1p1P4/3R4/PP3PPP/6K1 w - - -solution Rg3+ -id "ECM.530" -go -setboard rn4k1/pp2rpb1/2pNb1pp/4p3/P3Pq2/2P2N1P/1PB1QPP1/R3R1K1 w - - -solution Nf5 -id "ECM.531" -go -setboard 3r1rk1/pp3ppp/8/2p5/4PB1n/P1P2qPP/Q3RP1K/6R1 b - - -solution Qg2+ -id "ECM.532" -go -setboard r4rn1/ppp1q3/2bp2kp/6P1/3QpP2/2N5/PPP1B3/2KR3R w - - -solution f5+ -id "ECM.533" -go -setboard 3qr2r/1p1bppk1/3p2p1/p1nP1PQp/3N3R/1P4PP/P1P3BK/4R3 w - - -solution Ne6+ -id "ECM.534" -go -setboard 2rq1rk1/1b1nbpp1/1p5p/p2pNB2/3p1N2/4P3/PPQ2PPP/2RR2K1 w - - -solution Bh7+ -id "ECM.535" -go -setboard 8/5pkp/2pp2p1/4P3/1P2P3/2QBq2P/2PnNnPK/8 b - - -solution Nf1+ -id "ECM.536" -go -setboard r3r2k/pb2q1p1/1p2Nn1p/2b5/2B5/2P2P2/PPQ3PP/4RR1K w - - -solution Nf4 -id "ECM.537" -go -setboard r1b2nk1/ppq2ppp/8/1BP1p1NQ/5p2/8/PP3PPP/3R2K1 w - - -solution Rd7 -id "ECM.538" -go -setboard 6k1/5qp1/pN5p/3pRp2/3QnP2/2r4P/r5P1/6RK b - - -solution Qh5 -id "ECM.539" -go -setboard 5r1n/r2q1pkp/3p1npN/NppPp2P/1P2P3/2P1Q1P1/5P2/R3K2R w KQ - -solution Nf5+ -id "ECM.540" -go -setboard 6rk/p2b4/3p1Nqp/2pPpn2/P1P4p/5P1P/Q2B1NP1/6RK b - - -solution Ng3+ -id "ECM.541" -go -setboard r4r1k/pppb2pp/8/3q4/2N5/P1P2nQ1/BP2R2P/R6K b - - -solution Ne1+ -id "ECM.542" -go -setboard r5rk/1pp1n1pp/p1n1b1q1/3p1p2/7R/2QB1N2/PB3PPP/4R1K1 w - - -solution Rxe6 -id "ECM.543" -go -setboard 8/4Ppk1/6p1/1p6/1Pb1Q3/6pq/8/6K1 w - - -solution Qe5+ -id "ECM.544" -go -setboard 3R4/4Qpk1/4p1p1/7p/7K/5rP1/5P2/2q5 w - - -solution Qf8+ -id "ECM.545" -go -setboard 8/5pk1/p6p/2R5/3q2p1/6P1/2Q3PK/4r3 w - - -solution Rg5+ -id "ECM.546" -go -setboard 1Q4R1/8/P1b2p2/2p2k2/2P5/5p2/1r5P/4R1K1 b - - -solution f2+ -id "ECM.547" -go -setboard 5r1k/1q2R3/8/6Q1/8/8/6P1/7K b - - -solution Rf1+ -id "ECM.548" -go -setboard 8/4p3/8/3P3p/P2pK3/6P1/7b/3k4 w - - -solution d6 -id "ECM.549" -go -setboard 3Q4/k3K3/q7/2N1P3/1P6/5r2/8/8 b - - -solution Rf7+ -id "ECM.550" -go -setboard 3k4/p1p5/8/2p1p3/4P3/PpPP2K1/1P6/8 b - - -solution c4 -id "ECM.551" -go -setboard 6r1/5p1k/4p3/3pQp2/1p1P1P1q/1P3RR1/7r/5K2 w - - -solution Rxg8 -id "ECM.552" -go -setboard r1b2rk1/ppp2ppp/2n3q1/b2N2N1/2BP4/4p2P/PPP3P1/R2Q1RK1 w - - -solution Nxf7 -id "ECM.553" -go -setboard 1r4k1/3qb2p/pr1pppn1/2p2N2/2P2PQ1/1P4P1/P2R2BP/R5K1 w - - -solution Bd5 -id "ECM.554" -go -setboard r6r/pR1nqkpp/2p1p3/3p4/6Q1/2b4P/P1PP1PP1/2B1R1K1 w - - -solution Rxe6 -id "ECM.555" -go -setboard 1k1r3r/ppq2pnp/1npbb1p1/3p4/3P1NP1/2NBP2P/PPQ2P1B/1KR4R w - - -solution Nb5 -id "ECM.556" -go -setboard 5rk1/p1p1br2/1p1p2qp/4p3/2PPn1pP/3Q2P1/PPN1P1K1/2RR2B1 b - - -solution Bxh4 -id "ECM.557" -go -setboard r5r1/4q2k/p2p3p/5p2/4p3/P2QR3/1PP2PPP/4R1K1 w - - -solution Rxe4 -id "ECM.558" -go -setboard r1b2rk1/4bnpp/2p5/pp2q3/4N3/PQ2B1P1/1PP3BP/4RRK1 w - - -solution Ng5 -id "ECM.559" -go -setboard 2rr2k1/1p3p2/p2n2p1/P2PnNq1/3QP1p1/1RN5/6PP/5RK1 b - - -solution Nf3+ -id "ECM.560" -go -setboard r3k1r1/pb1qpp1p/1pnp1n1B/2p2R2/3PP3/2P3PN/P1P3BP/R2Q3K b q - -solution Nxe4 -id "ECM.561" -go -setboard 2r2rk1/pp2bppp/3q1n2/3nNRB1/3P4/1B5Q/PP4PP/3R2K1 w - - -solution Nxf7 -id "ECM.562" -go -setboard r1bq1rk1/1pp2ppn/1pnp3p/3N4/2B1PN2/3P4/PPP3PP/R2Q1RK1 w - - -solution Ng6 -id "ECM.563" -go -setboard r1b2rk1/pp3p1p/2pq2pn/2nNN3/2B1QP2/8/P1PP2PP/R1B1K2R w KQ - -solution Nxf7 -id "ECM.564" -go -setboard 1r2r1k1/1bn2pbp/pp1q1np1/2pPN1B1/P1B5/2N4P/1P1Q1PP1/R3R1K1 w - - -solution Nxf7 -id "ECM.565" -go -setboard 1rb2bk1/2n2qp1/p2p3p/R1pP1p2/2B5/1PBQ1N1P/2P2PP1/6K1 w - - -solution Rxc5 -id "ECM.566" -go -setboard r1b3k1/2pq2pp/p4p2/1p1Nr3/2n2R2/1B6/PPQ2PPP/R5K1 w - - -solution Rxc4 -id "ECM.567" -go -setboard 2rq1rk1/3bbppp/p3pn2/1p1pN3/2nP1B2/P1NBP2Q/1P3PPP/2R2RK1 w - - -solution Nxd5 -id "ECM.568" -go -setboard 2r1r1k1/pp1b1ppp/1q6/3P4/7N/Pn1Q4/1P3PPP/1RB2RK1 b - - -solution Nxc1 -id "ECM.569" -go -setboard 1r4nk/3b2bp/3p4/3P3p/2pNPq2/P1N2p1P/1P3Q2/1R4KR b - - -solution Rxb2 -id "ECM.570" -go -setboard 2r5/p3kpp1/P2p4/1P2pP1p/1P2PqnP/6R1/r2N2P1/3RQ1K1 b - - -solution Rxd2 -id "ECM.571" -go -setboard 2br2k1/1p2qppp/r1n1p3/pQ1nN3/P2P4/2B3P1/1P3PBP/2RR2K1 w - - -solution Qxa6 -id "ECM.572" -go -setboard 5rk1/3q2bp/3pb2r/1p1n1p2/3p1P2/P2B1QN1/1P1B2PP/2R1R1K1 w - - -solution Nxf5 -id "ECM.573" -go -setboard 4r1k1/1ppQr1p1/1p1p1pP1/3P3p/7P/1P3q2/P1P5/1KRR4 w - - -solution Re1 -id "ECM.574" -go -setboard 2Rr2k1/7p/4pqp1/p4p2/1p1P1Q1P/8/PP3PP1/6K1 w - - -solution Qe5 -id "ECM.575" -go -setboard 4r1kb/5p1p/6pB/3Np3/4n3/P3Q1PP/2q2P2/R5K1 w - - -solution Rc1 -id "ECM.576" -go -setboard 5rk1/bp4p1/p2rp2p/8/8/2PN4/PP1R1PPP/3R2K1 b - - -solution e5 -id "ECM.577" -go -setboard 2q1rk2/QR3ppp/1pPpb3/1P2p1b1/4P3/6P1/3N1PBP/6K1 w - - -solution Bh3 -id "ECM.578" -go -setboard n1bq3k/pp4bp/6p1/5pP1/3p1P2/1B1P4/PB3Q1P/4R1K1 w - - -solution Bxd4 -id "ECM.579" -go -setboard r3r1k1/p2n4/3p1pp1/qnp3B1/1p2P1P1/1P3P2/PP2N2Q/1K1R1B2 w - - -solution Nf4 -id "ECM.580" -go -setboard 6k1/pb3ppp/1p2p3/4P3/5P2/P1r5/1q4PP/1B2Q1RK b - - -solution Rc1 -id "ECM.581" -go -setboard 2rq1kbR/4R1p1/3p3r/pp1P1Pp1/6P1/3B4/PPP1Q3/6K1 w - - -solution f6 -id "ECM.582" -go -setboard 2r5/5kb1/4ppp1/pp1qB3/2rP4/1RP2R1Q/P4PP1/6K1 b - - -solution Rh8 -id "ECM.583" -go -setboard 4k3/4Bp2/1p2nP1P/2p5/2K1b1B1/8/P7/8 w - - -solution Bf3 -id "ECM.584" -go -setboard 3rr1k1/ppp1qp1p/3b2p1/4np1P/1P1N1Pn1/P1P4R/1B1NB3/R2Q1K2 b - - -solution Nf3 -id "ECM.585" -go -setboard 1k1r3r/ppqb4/5Ppp/3p4/P2np3/3BR1Q1/2PB1PPP/R5K1 w - - -solution Rxe4 -id "ECM.586" -go -setboard r2q1rk1/pb1nbpp1/1pp1pn1p/3pN3/2PPP3/2N1B1P1/PP3PBP/R2Q1RK1 w - - -solution Nxc6 -id "ECM.587" -go -setboard 8/2R2pbk/6pp/1p2pq2/1P2Qn2/3P3P/3B1PP1/r3N1K1 b - - -solution Nxd3 -id "ECM.588" -go -setboard 2r2rk1/pp2pp1p/2np2p1/q4P2/2PBP1b1/2N5/PP1Q2PP/R4RK1 w - - -solution h3 -id "ECM.589" -go -setboard r2b1rnk/1p4qp/p1p1b1p1/2PpNp2/PP1PnP1Q/3BB2R/4N1PP/R6K w - - -solution Qxh7+ -id "ECM.590" -go -setboard r1b2rk1/1p3pbp/2p3p1/p1q5/1P6/1Q4P1/1P2PPBP/R1B1R1K1 b - - -solution Be6 -id "ECM.591" -go -setboard 4r1k1/r1q2p1p/p5pB/1pbBpN1n/1n2P3/5Q2/PP3PPP/R1R3K1 w - - -solution Rxc5 -id "ECM.592" -go -setboard r1r3k1/ppnbb1pp/2ppp1q1/1P3p2/P1PPn3/2N2NP1/2Q1PPBP/1RB2RK1 w - - -solution Nxe4 -id "ECM.593" -go -setboard r1b1k2r/2q1bppp/p3p3/1p1nP1B1/3Q4/2N5/PPP1B1PP/2KR3R w kq - -solution Bxe7 -id "ECM.594" -go -setboard 1rb1r1k1/p1p1qppp/2pb4/8/2P3n1/4P1P1/PB2BP1P/R1QN1RK1 b - - -solution Nxh2 -id "ECM.595" -go -setboard 2rqk1nr/pp3ppp/2n1p3/2b5/3N4/2NQB1P1/PP2PPKP/R4R2 w k - -solution Nxe6 -id "ECM.596" -go -setboard rnb2rk1/pp4pp/2pb1n2/3N1p1q/2P5/3N2P1/P1Q1PPBP/R1B2RK1 w - - -solution Bf4 -id "ECM.597" -go -setboard 8/8/1p1k4/5ppp/PPK1p3/6P1/5PP1/8 b - - -solution f4 -id "ECM.598" -go -setboard 8/5pp1/7p/5P1P/2p3P1/2k5/5P2/2K5 w - - -solution f6 -id "ECM.599" -go -setboard 7k/8/1p1ppp1p/1Pp5/2P2P2/8/3P2PP/6K1 w - - -solution f5 -id "ECM.600" -go -setboard 8/p5k1/2p2p2/4p1p1/1p2P3/1P4PP/1PP5/5K2 b - - -solution g4 -id "ECM.601" -go -setboard 8/5K2/kp6/p1p5/P2p4/1P3P2/2P5/8 b - - -solution b5 -id "ECM.602" -go -setboard 8/2k3p1/2p4p/5P2/2K3PP/8/8/8 w - - -solution g5 -id "ECM.603" -go -setboard 8/1kp1b3/1p4K1/4P2p/P1P3p1/5pP1/P4P2/4B3 b - - -solution h4 -id "ECM.604" -go -setboard 8/8/4b1p1/2Bp3p/5P1P/1pK1Pk2/8/8 b - - -solution g5 -id "ECM.605" -go -setboard 8/8/3K1k2/5p1p/4p1p1/4P1P1/5PP1/8 b - - -solution f4 -id "ECM.606" -go -setboard r1b5/p2k1r1p/3P2pP/1ppR4/2P2p2/2P5/P1B4P/4R1K1 w - - -solution Bxg6 -id "ECM.607" -go -setboard 6r1/1p3k2/pPp4R/K1P1p1p1/1P2Pp1p/5P1P/6P1/8 w - - -solution Rxc6 -id "ECM.608" -go -setboard 1k2b3/4bpp1/p2pp1P1/1p3P2/2q1P3/4B3/PPPQN2r/1K1R4 w - - -solution f6 -id "ECM.609" -go -setboard 2kr3r/ppp1qpp1/2p5/2b2b2/2P1pPP1/1P2P1p1/PBQPB3/RN2K1R1 b Q - -solution Rh1 -id "ECM.610" -go -setboard 1r3qk1/pb3p1p/1pn2PpQ/2pN4/3r4/5B2/PPP4P/4RRK1 w - - -solution Ne7+ -id "ECM.611" -go -setboard 4r1k1/5p1p/3q2p1/1p1P4/1P6/2p4P/2Q1nPB1/4RK2 b - - -solution Ng3+ -id "ECM.612" -go -setboard 6k1/2q3p1/1n2Pp1p/pBp2P2/Pp2P3/1P1Q1KP1/8/8 w - - -solution e5 -id "ECM.613" -go -setboard 5r2/pp1RRrk1/4Qq1p/1PP3p1/8/4B3/1b3P1P/6K1 w - - -solution Qxf7+ -id "ECM.614" -go -setboard 6k1/1q2rpp1/p6p/P7/1PB1n3/5Q2/6PP/5R1K w - - -solution b5 -id "ECM.615" -go -setboard r5r1/8/bRP1pk1p/3pNp2/5P2/7P/PR4P1/6K1 w - - -solution Rxa6 -id "ECM.616" -go -setboard 3r2k1/p6p/b2r2p1/2qPQp2/2P2P2/8/6BP/R4R1K w - - -solution Rxa6 -id "ECM.617" -go -setboard 8/p3q1k1/6p1/1p3nP1/2pp1QBr/P2P4/1PP2K2/R7 b - - -solution Qe3+ -id "ECM.618" -go -setboard 7k/4b2p/5p1P/5PP1/1pNp1P2/1P1B4/2P2K2/r7 w - - -solution g6 -id "ECM.619" -go -setboard 5r1k/1p1b2rP/p2p4/3P3B/P2P4/5pP1/1P2pP2/R3R1K1 b - - -solution Rxg3+ -id "ECM.620" -go -setboard 6B1/2p2pp1/2p5/p3K3/1k6/nP4P1/5P1P/8 b - - -solution Nc4+ -id "ECM.621" -go -setboard 3q1k2/5p2/p5pN/1b2Q2P/8/8/5PPK/8 w - - -solution Qh8+ -id "ECM.622" -go -setboard 6k1/p3b1pp/4p3/4Pp2/Pp1r1P1P/1P4P1/2p2R2/5RK1 b - - -solution Rc4 -id "ECM.623" -go -setboard 4r2k/3n1Qpp/1pRP1p2/p4P2/1p1P2P1/6rP/P3q1B1/6RK w - - -solution Qe6 -id "ECM.624" -go -setboard 2r2rk1/p1N2p1p/2P1p1p1/1Pp3q1/3b4/5Q2/P1P3PP/4RR1K w - - -solution Nxe6 -id "ECM.625" -go -setboard 3r1r2/pp1P3k/4Rbp1/1BP2p1p/8/7P/P4KP1/3R4 w - - -solution Ba6 -id "ECM.626" -go -setboard 3rr1k1/pppRn1pp/4Pp2/1P5q/1QB5/4P3/P4P1P/4K1R1 w - - -solution Qxe7 -id "ECM.627" -go -setboard rn1q2k1/pp3pb1/3p2pp/2pP2N1/3r1P2/7Q/PP4PP/R1B2RK1 w - - -solution Nxf7 -id "ECM.628" -go -setboard 8/6Bp/6p1/2k1p3/4PPP1/1pb4P/8/2K5 b - - -solution b2+ -id "ECM.629" -go -setboard 5bk1/r4ppp/1r1P1n2/2p2N2/b7/2B3P1/5PBP/R3R1K1 w - - -solution Bxf6 -id "ECM.630" -go -setboard 8/8/8/P2k1p2/2N5/1pb2P2/4P3/2K5 w - - -solution a6 -id "ECM.631" -go -setboard 3qr1k1/p4ppp/1p1P4/2r1nN2/4P2n/P7/1B4P1/3QRRK1 w - - -solution d7 -id "ECM.632" -go -setboard 6k1/5pbp/6p1/2p1r3/1pPr4/3n2NP/1PB2PP1/1R1R2K1 b - - -solution Nxb2 -id "ECM.633" -go -setboard 4r3/p4pk1/6p1/P1pB2Pp/2P3b1/2p1N1P1/Qb1r1P2/6K1 b - - -solution Rxe3 -id "ECM.634" -go -setboard 2r1rbk1/p1Bq1ppp/Ppn1b3/1Npp4/B7/3P2Q1/1PP2PPP/R4RK1 w - - -solution Nxa7 -id "ECM.635" -go -setboard r4rk1/ppq3pp/2p1Pn2/4p1Q1/8/2N5/PP4PP/2KR1R2 w - - -solution Rxf6 -id "ECM.636" -go -setboard r3qr1k/1b4pp/p2RQ3/Bp2p3/4p3/6P1/PPP2P1P/3R2K1 b - - -solution Qh5 -id "ECM.637" -go -setboard 8/6kP/6p1/1p1pP3/pP6/P1n2K2/2N5/8 w - - -solution e6 -id "ECM.638" -go -setboard 8/Pb6/8/8/7K/5k1p/6pB/8 b - - -solution g1=Q -id "ECM.639" -go -setboard 8/pR4pk/1b6/2p5/N1p5/8/PP1r2PP/6K1 b - - -solution Rxb2 -id "ECM.640" -go -setboard 6k1/p4pp1/Pp2r3/1QPq3p/8/6P1/2P2P1P/1R4K1 w - - -solution cxb6 -id "ECM.641" -go -setboard 6k1/p4pbp/Bp2p1p1/n2P4/q3P3/B1rQP3/P5PP/5RK1 w - - -solution dxe6 -id "ECM.642" -go -setboard 5r2/R4p2/5P1k/4PK2/1p6/8/8/8 w - - -solution Rxf7 -id "ECM.643" -go -setboard 4r2k/pppb2pp/2np2q1/3B4/2P2P2/1PB1R1P1/PQ5P/6K1 w - - -solution f5 -id "ECM.644" -go -setboard 1r6/3r1Pk1/p2p1np1/5q2/1p3P2/1B5R/PPP4Q/1K1R4 w - - -solution Rh8 -id "ECM.645" -go -setboard 8/2k5/2p5/2pb2K1/pp4P1/1P1R4/P7/8 b - - -solution Bxb3 -id "ECM.646" -go -setboard 2r5/1r5k/1P3p2/PR2pP1p/4P2p/2p1BP2/1p2n3/4R2K b - - -solution Nd4 -id "ECM.647" -go -setboard 4r2k/p2qr1pp/1pp2p2/2p1nP1N/4R3/1P1P2RP/1PP2QP1/7K w - - -solution Rxg7 -id "ECM.648" -go -setboard 6k1/p4p2/5Ppp/3RP3/Pr4P1/2p2K2/7P/8 w - - -solution Rd8+ -id "ECM.649" -go -setboard 8/1R2P3/6k1/3B4/2P2P2/1p2r3/1Kb4p/8 w - - -solution Be6 -id "ECM.650" -go -setboard 2kr2r1/pp2bQ1p/2b1P3/2qN4/8/1B2p2P/PPP3P1/3R1R1K b - - -solution e2 -id "ECM.651" -go -setboard r1b2rk1/1p2qppp/p3p3/2n5/3N4/3B1R2/PPP1Q1PP/R5K1 w - - -solution Bxh7+ -id "ECM.652" -go -setboard 3r1rk1/pp1q1ppp/3pn3/2pN4/5PP1/P5PQ/1PP1B3/1K1R4 w - - -solution Bb5 -id "ECM.653" -go -setboard r2qrbk1/5ppp/pn1p4/np2P1P1/3p4/5N2/PPB2PP1/R1BQR1K1 w - - -solution Bxh7+ -id "ECM.654" -go -setboard 6rk/3nrpbp/p1bq1npB/1p2p1N1/4P1PQ/P2B3R/1PP1N2P/5R1K w - - -solution Nxh7 -id "ECM.655" -go -setboard 1rb2rk1/3nqppp/p1n1p3/1p1pP3/5P2/2NBQN2/PPP3PP/2KR3R w - - -solution Bxh7+ -id "ECM.656" -go -setboard 2k5/ppp3pp/8/NQ2n2q/2Pp1n2/R4bP1/1P3P1P/4R1K1 b - - -solution Qxh2+ -id "ECM.657" -go -setboard 5rrk/1p3qpp/p3pn2/2PpBp2/3P1P1Q/P1P1R2R/2b1B1PP/6K1 w - - -solution Qxh7+ -id "ECM.658" -go -setboard r4rk1/1bq2ppp/p1p1p3/2b1P1B1/3p2Q1/3B4/PPP2PPP/R3R1K1 w - - -solution Bxh7+ -id "ECM.659" -go -setboard r4rk1/pb1q1ppp/2N1p3/2Rn4/3P4/3BPQ2/1P3PPP/2R3K1 w - - -solution Bxh7+ -id "ECM.660" -go -setboard r5rk/1p1q1p1p/5pn1/p2p1N1Q/3P2P1/PP2R3/5P1P/5RK1 w - - -solution Qxh7+ -id "ECM.661" -go -setboard 2r2r2/p2qppkp/3p2p1/3P1P2/2n2R2/7R/P5PP/1B1Q2K1 w - - -solution Rxh7+ -id "ECM.662" -go -setboard 1r1qr1k1/5p1p/1n2p1p1/pp1pP1P1/2pP1BB1/PnP3P1/1P3PK1/1R1Q3R w - - -solution Rxh7 -id "ECM.663" -go -setboard 4rrk1/2q1bppp/p2p4/1p1Pn3/3B1R2/P2B2Q1/1PP3PP/5R1K w - - -solution Bxh7+ -id "ECM.664" -go -setboard r1b2rk1/2q1bppp/p1n1p3/1p1np1P1/5P2/1NNBBQ2/PPP4P/R4RK1 w - - -solution Bxh7+ -id "ECM.665" -go -setboard 5r1k/ppp2qnp/1n1p1N1Q/3Ppb2/2P4P/7B/PP6/2KR2R1 w - - -solution Nxh7 -id "ECM.666" -go -setboard r4rk1/pp2q1p1/4b2p/2ppb3/6n1/2P3N1/PPQBBPPP/R4RK1 b - - -solution Nxh2 -id "ECM.667" -go -setboard b3r1k1/2rN1ppp/2n1p3/p7/P3BP2/1R6/q1P2QPP/3R2K1 w - - -solution Bxh7+ -id "ECM.668" -go -setboard 2r1qrk1/3n3p/b3pPp1/4P3/1pp1nBN1/pP4PQ/P1P2PK1/3RR3 w - - -solution Qxh7+ -id "ECM.669" -go -setboard r1b1rnk1/pp1nb1pp/2p1pp2/q3N3/2PP1P2/3BP1N1/PBQ3PP/R4RK1 w - - -solution Bxh7+ -id "ECM.670" -go -setboard 4r1k1/2q2r1p/p3bQ2/1p4Bp/2np4/8/PPB2PP1/3RR1K1 w - - -solution Bxh7+ -id "ECM.671" -go -setboard 5rk1/pbrn1ppp/1ppN1q2/2P1p3/3P4/1PRB4/P3QPPP/5RK1 w - - -solution Bxh7+ -id "ECM.672" -go -setboard 2rqr1k1/1p1bbppp/p3p3/n7/3P2Q1/2PB1N2/P4PPP/R1B1R1K1 w - - -solution Bxh7+ -id "ECM.673" -go -setboard r1b2r1k/ppppq1pp/2n1n3/6N1/2B2P2/4B3/PPP3PP/R2Q1RK1 w - - -solution Nxh7 -id "ECM.674" -go -setboard rn1q1rk1/pb1p1ppp/4p3/2pnP3/1bp5/2N2N2/PP3PPP/RBBQK2R w KQ - -solution Bxh7+ -id "ECM.675" -go -setboard r2r2k1/p1p2p1p/4pPp1/1Pqb4/8/7R/1PB2PPP/3QR1K1 w - - -solution Rxh7 -id "ECM.676" -go -setboard r1b2rk1/1pq2ppp/p1p1p3/2n1P3/2N2P2/3B4/PPP3PP/R2Q1RK1 w - - -solution Bxh7+ -id "ECM.677" -go -setboard rnq3rk/4bp1p/p1p3pQ/1p1pP3/1P1N1B2/2P3R1/1P3PPP/R5K1 w - - -solution e6 -id "ECM.678" -go -setboard rn3rk1/pp1bppbp/1qp3p1/4P1N1/PP1PB3/2P1B3/4Q1PP/R4RK1 w - - -solution Nxh7 -id "ECM.679" -go -setboard 3rr1k1/1pq1nppp/p1p2b2/4pB2/2QPP3/P1P1B3/1P4PP/3R1RK1 w - - -solution Bxh7+ -id "ECM.680" -go -setboard 1r2nrkb/2n2p1p/p2p1Pp1/P1pPp1P1/1pP1P1q1/1P1BB1N1/3Q4/2KR3R w - - -solution Rxh7 -id "ECM.681" -go -setboard 2rrn1k1/2q2ppp/p2pp3/1p2P1P1/4B3/P5Q1/1PP3PP/R4R1K w - - -solution Bxh7+ -id "ECM.682" -go -setboard r2q3r/2pkb1p1/p2p1n2/4p1p1/Pp2P1P1/1QP5/1P1P2PP/RNB2RK1 b - - -solution Rxh2 -id "ECM.683" -go -setboard 2rq1rk1/pp1bnpbp/4p1p1/3pP1N1/3P2Q1/2PB4/P4PPP/R1B1R1K1 w - - -solution Nxh7 -id "ECM.684" -go -setboard 2rq1bk1/1br2p1p/p2p2p1/1p1P4/4Q3/PP3N2/1BP5/1K1R3R w - - -solution Rxh7 -id "ECM.685" -go -setboard r2qn1k1/pb4Pp/1pn5/2p5/2P2p2/P1PB4/5PPP/R2Q1RK1 w - - -solution Bxh7+ -id "ECM.686" -go -setboard r4rk1/pp1n1ppp/3qp3/3nN1P1/b2P4/P2B1Q2/3B1P1P/1R2R1K1 w - - -solution Bxh7+ -id "ECM.687" -go -setboard r2r2k1/ppqbbppp/4pn2/6N1/n1P4P/3B1N2/PP2QPP1/1KBR3R w - - -solution Nxh7 -id "ECM.688" -go -setboard r5k1/6bp/2q1p1p1/p2pP3/3P4/1rP2QP1/3B1PK1/2R4R w - - -solution Rxh7 -id "ECM.689" -go -setboard r6r/4ppk1/p2p1bp1/B2p4/3P2p1/QP2P3/P1R1qPPP/2R3K1 b - - -solution Rxh2 -id "ECM.690" -go -setboard 4nrk1/r4p1p/p2p2pQ/2pPpNPP/qpn1P3/5P1R/PPP1N3/2K3R1 w - - -solution Qxh7+ -id "ECM.691" -go -setboard r3k2r/1b1n1p2/pq1p1bp1/1p4p1/P3P3/1NN5/1PP3PP/R2QRB1K b kq - -solution Rxh2+ -id "ECM.692" -go -setboard r2qrnk1/4bppp/b1p5/1p1p2P1/p2P1N1P/2NBP3/PPQ2P2/2K3RR w - - -solution Bxh7+ -id "ECM.693" -go -setboard rn1q1rk1/pppbb1pp/4p3/3pP1p1/3P3P/2NB4/PPP2PP1/R2QK2R w KQ - -solution Bxh7+ -id "ECM.694" -go -setboard 1r2nr1k/6pp/pp1p1p2/2pPnN2/q1P1PB2/5PR1/4K1P1/2Q4R w - - -solution Bxe5 -id "ECM.695" -go -setboard 3r1rk1/4qp1p/p1bb2p1/2p5/3P4/1P6/PBQN1PPP/2R2RK1 b - - -solution Bxh2+ -id "ECM.696" -go -setboard r2q1rk1/3n1ppp/8/1pbP2P1/p1N4P/PnBBPQ2/5P2/R3K2R w KQ - -solution Bxh7+ -id "ECM.697" -go -setboard 2k3r1/ppq2p1p/n1Pb1p2/8/6r1/4BN1b/PPQ2PPP/R3RBK1 b - - -solution Bxh2+ -id "ECM.698" -go -setboard 3qr1k1/1br2ppp/p2b4/8/PpNp4/3P4/1PP2PPP/R1BQ1RK1 b - - -solution Bxh2+ -id "ECM.699" -go -setboard 3r2k1/p1R2p2/4pQp1/1q5p/5P1P/1PR5/2Pr2P1/6K1 b - - -solution Rxg2+ -id "ECM.700" -go -setboard r1b4r/3p1kp1/pp3q1p/4RP2/8/B7/P5PP/4QRK1 w - - -solution Qe4 -id "ECM.701" -go -setboard 1r2rnk1/2R2bpp/p2q4/1p1N2R1/5P2/1Q1B3P/PP4P1/7K w - - -solution Rxg7+ -id "ECM.702" -go -setboard 3r2k1/pb5p/1p2qpp1/8/2p5/1P1nP3/P1N2PPP/1Q1R1R1K b - - -solution Bxg2+ -id "ECM.703" -go -setboard 4rrk1/2qb2pp/p5P1/1p2p3/1b2P3/2N5/PPPQ4/1K1R2R1 w - - -solution gxh7+ -id "ECM.704" -go -setboard 3q1k2/pp3ppn/2p1n2p/4pN1N/P3P2P/5Q2/1PP2PP1/6K1 w - - -solution Nhxg7 -id "ECM.705" -go -setboard 2r2r1k/3b1pb1/p3pp1p/q2p1P1B/8/2N2RR1/P1PQ2PP/7K w - - -solution Rxg7 -id "ECM.706" -go -setboard 5bk1/1p4p1/4N1R1/3p4/p1r2P1q/Pr1QP2P/1P6/1K4R1 w - - -solution Rxg7+ -id "ECM.707" -go -setboard 2r1r1k1/5ppp/p3pn2/1pb1N3/2P5/1PQ3R1/PB2qPPP/3R2K1 w - - -solution Rxg7+ -id "ECM.708" -go -setboard 7k/1bp1P1p1/3p2rp/1pb2p1q/5P2/1BP2N1P/1P4P1/R1B2R1K b - - -solution Rxg2 -id "ECM.709" -go -setboard r1q1bk1r/1p3pp1/4pn1p/p3Q3/P1P2N2/1B4R1/1P3PPP/4R1K1 w - - -solution Rxg7 -id "ECM.710" -go -setboard r4rk1/p2n2p1/1q1Qpn1p/1P6/P6B/2p5/2B1KP1P/R5R1 w - - -solution Rxg7+ -id "ECM.711" -go -setboard r2qr1k1/1p3ppp/2pb4/2Np4/1P1P2bn/3BP3/2QN1PPP/1RR3K1 b - - -solution Nxg2 -id "ECM.712" -go -setboard r3r1k1/pp3pp1/3p4/2q4p/2P5/1PB2Q1P/n4PP1/3R1RK1 w - - -solution Bxg7 -id "ECM.713" -go -setboard r5k1/pn1q1rpp/2pp4/5R1N/bP6/4BQ2/P4PPP/2R3K1 w - - -solution Nxg7 -id "ECM.714" -go -setboard 3r1rk1/5pp1/pq1n1n1p/2pPR3/2B2P2/1PBQ2RP/P5P1/6K1 w - - -solution Rxg7+ -id "ECM.715" -go -setboard rn3rk1/ppq2ppp/2pb1nb1/5N2/2BP4/8/PPP1N1P1/R1B1QR1K w - - -solution Nxg7 -id "ECM.716" -go -setboard r1qb1r1k/2p3pp/p1n1bp2/1p1Np2Q/P3P3/1BP3R1/1P3PPP/R1B3K1 w - - -solution Rxg7 -id "ECM.717" -go -setboard r4rk1/5ppp/p2pbb2/3B3Q/qp2p3/4B3/PPP2P1P/2KR2R1 w - - -solution Rxg7+ -id "ECM.718" -go -setboard 4q1k1/1b3r1p/p4pp1/1p6/2P2n2/1P3N2/1B3PPP/R2Q3K b - - -solution Nxg2 -id "ECM.719" -go -setboard r2r3k/5bp1/2p2N2/5P1p/3q3Q/3B2R1/n5PP/3R3K w - - -solution Rxg7 -id "ECM.720" -go -setboard 4rrk1/2pn2pb/p1p1qp2/1pb1pN2/P3P1PN/1P1P4/1BP2PK1/R2Q3R w - - -solution Nxg7 -id "ECM.721" -go -setboard r3r1k1/p3bppp/q1b2n2/5Q2/1p1B4/1BNR4/PPP3PP/2K2R2 w - - -solution Rg3 -id "ECM.722" -go -setboard r4rk1/1p1q1ppp/p1b4B/8/2R3R1/P2P4/1b1N1QPP/6K1 w - - -solution Bxg7 -id "ECM.723" -go -setboard rq3rk1/3b1ppp/p2bp3/3pB2Q/8/1B5P/PP3PP1/2RR2K1 w - - -solution Bxg7 -id "ECM.724" -go -setboard 4kbr1/pp1bpp1p/3p1p2/3N4/1PBQP3/q7/P1r2PPP/R4RK1 b - - -solution Rxg2+ -id "ECM.725" -go -setboard 2r3k1/1p2R1p1/p3n2p/2pq4/7P/1P1P2P1/PB1Q3K/8 w - - -solution Bxg7 -id "ECM.726" -go -setboard 2rr2k1/4bppp/p1n1p3/3q4/1p1P2N1/2P3R1/P3QPPP/2B2RK1 w - - -solution Nh6+ -id "ECM.727" -go -setboard rq1r1bk1/1b3pp1/3pn2p/1n2BN1P/1P2P3/3R1NP1/3Q1PB1/2R3K1 w - - -solution Bxg7 -id "ECM.728" -go -setboard r1b5/ppqn2bk/3R2pp/2p2p2/2P1rN2/4BN2/PPQ2PPP/4R1K1 w - - -solution Rxg6 -id "ECM.729" -go -setboard 3q1r2/1p1b1pk1/pn5p/3pN1pQ/3P3P/2r3B1/P4PP1/3RR1K1 w - - -solution Nxf7 -id "ECM.730" -go -setboard r1bqkbnr/pp2ppp1/2p4p/3n2N1/2BP4/5N2/PPP2PPP/R1BQK2R w KQkq - -solution Nxf7 -id "ECM.731" -go -setboard r2qr1k1/1ppb1p1p/p1np2p1/7Q/3PP2b/1B2N2P/PP3PP1/R1B2RK1 w - - -solution Bxf7+ -id "ECM.732" -go -setboard 2r3k1/pbq1np1p/1p1b1Bp1/8/6QP/2P2N2/B4PP1/4R1K1 w - - -solution Bxf7+ -id "ECM.733" -go -setboard r3r1k1/1bqn1ppp/1pp2p2/8/3P4/1B4N1/PP3PPP/R2QR1K1 w - - -solution Bxf7+ -id "ECM.734" -go -setboard r3r1k1/pp3ppp/8/1Nbp3q/3NnBn1/2P5/PPQ2PPP/3R1RK1 b - - -solution Ngxf2 -id "ECM.735" -go -setboard r2r2k1/1b2qpp1/1p2n3/p1b1p2p/4P1n1/PP1B2PP/1B1NQP2/R3NRK1 b - - -solution Nxf2 -id "ECM.736" -go -setboard r2qr1k1/1b2bp1p/p3p1p1/1p2N1Bn/3P4/P6Q/1P3PPP/RB2R1K1 w - - -solution Nxf7 -id "ECM.737" -go -setboard r5k1/1pp3pp/1b1p1r2/pN1P1n2/P1P3nq/6N1/1P1B1PQP/4RRK1 b - - -solution Nxf2 -id "ECM.738" -go -setboard 2rqnk1r/pp2bpp1/2p1pn1p/2P1N2P/3P1BP1/3BQ3/PP6/2K2R1R w - - -solution Nxf7 -id "ECM.739" -go -setboard 2rq1rk1/1b2bppp/p1n5/1p1BN3/5B2/P7/1P3PPP/R2Q1RK1 w - - -solution Nd7 -id "ECM.740" -go -setboard 3r1qrk/p1p5/bbp3pp/4p3/4Pp1N/2Pn3P/PPQN1PPB/R4RK1 b - - -solution Nxf2 -id "ECM.741" -go -setboard r2qr1k1/pb2bp1p/1pn1p1pB/8/2BP4/P1P2N2/4QPPP/3R1RK1 w - - -solution d5 -id "ECM.742" -go -setboard r3r1k1/1bq1nppp/p1np4/1ppBpN2/4P3/2PP1N2/PP3PPP/R2QR1K1 w - - -solution Bxf7+ -id "ECM.743" -go -setboard r5k1/p4ppp/3qpb2/1P2N3/1nBP4/1P5P/4QPP1/4R1K1 w - - -solution Nxf7 -id "ECM.744" -go -setboard r2qr1k1/1b1nbp1p/p3pp2/1p2N3/3P4/3B1N2/PP2QPPP/R2R2K1 w - - -solution Nxf7 -id "ECM.745" -go -setboard r3k2r/p1qb1p2/1p2p2p/3pPpN1/P1nP3Q/8/2P2PPP/R1B1R1K1 w kq - -solution Nxf7 -id "ECM.746" -go -setboard 3qNk2/5p1p/1r3p2/2p5/Q1pb4/3b2P1/P4PBP/4R1K1 b - - -solution Bxf2+ -id "ECM.747" -go -setboard 2r1r1k1/1pq1bp1p/p3pnp1/P2n2N1/7R/2P4P/1PB1QPP1/2B1R1K1 w - - -solution Nxf7 -id "ECM.748" -go -setboard r1bq2k1/pp1n1ppp/3b1n2/PQ1B3r/3N1P2/2N5/1PP3PP/R1B2RK1 w - - -solution Bxf7+ -id "ECM.749" -go -setboard 2r1r1k1/5ppp/pq3b2/2pB1P2/2p2B2/5Q1P/Pn3PP1/2R1R1K1 w - - -solution Bxf7+ -id "ECM.750" -go -setboard r4rk1/ppRn1p2/6pb/2P1pq1p/3N4/P1QPn1Pb/1B1NPP1P/4R1KB b - - -solution Qxf2+ -id "ECM.751" -go -setboard r3kr2/1b2qp2/pp2p2N/4p2Q/8/2n5/P3B1PP/3R1R1K w q - -solution Nxf7 -id "ECM.752" -go -setboard b2r1rk1/pq2bpp1/1p2p2p/4N2n/2P2R2/1PB2N2/1P2QPPP/4R1K1 w - - -solution Rxf7 -id "ECM.753" -go -setboard rqb1k2r/1p1nbp1p/p4pp1/8/1PBN1P2/P1N1P3/7P/2RQ1RK1 w kq - -solution Bxf7+ -id "ECM.754" -go -setboard r2r2k1/4ppbp/5np1/p1q5/QnB1P3/5N2/1P3PPP/R1B2RK1 w - - -solution Bxf7+ -id "ECM.755" -go -setboard 1r2q1k1/p3pp2/3p1bp1/2pP2N1/8/P5PB/2Q2PK1/1rBR4 w - - -solution Nxf7 -id "ECM.756" -go -setboard 2rqrbk1/pb1n1p1p/1p2pnp1/4N3/2BP1N2/1P2Q3/PB3PPP/3R1RK1 w - - -solution Nxf7 -id "ECM.757" -go -setboard 1r4k1/p4pp1/bqnrpn1p/2ppN3/2P2P1B/P3P3/1P2B1PP/1Q1R1RK1 w - - -solution Bxf6 -id "ECM.758" -go -setboard r1bqrbk1/5p1p/2pp2nB/pp5Q/4P3/PBNPR2P/1P4P1/R5K1 w - - -solution Bxf7+ -id "ECM.759" -go -setboard r1bqkb1r/1p3ppp/pn1P1n2/4p3/2B5/2N2N2/PP3PPP/R1BQ1RK1 w kq - -solution Bxf7+ -id "ECM.760" -go -setboard 2rqr1k1/pp2nppp/3p2b1/6B1/2BNn1Q1/P7/1PP2PPP/2KRR3 w - - -solution Bxf7+ -id "ECM.761" -go -setboard r3r1k1/pbq1ppbp/1pp2np1/4N3/3P4/2P5/PPB1QPPP/R1B1R1K1 w - - -solution Nxf7 -id "ECM.762" -go -setboard r1b2rk1/p6p/2p3Pb/3pp1q1/8/2PB4/PP3PP1/RN1Q1K1R b - - -solution Rxf2+ -id "ECM.763" -go -setboard r2rn3/1p3pk1/p1pNn1pp/q3P3/P7/1PN4P/2QR1PP1/3R2K1 w - - -solution Nxf7 -id "ECM.764" -go -setboard r1bqr1k1/1p1nnpb1/p5pp/2P1p1B1/B3N3/5N2/P4PPP/2RQR1K1 w - - -solution Nd6 -id "ECM.765" -go -setboard r1b1r1k1/pp2qpbp/2p3p1/2P5/3NnB2/7P/PPQ1PPB1/2RR2K1 b - - -solution Nxf2 -id "ECM.766" -go -setboard rn2r1k1/pp3p1p/3P2p1/2p1bbB1/2B5/2N5/Pq4PP/R2Q1RK1 w - - -solution Bxf7+ -id "ECM.767" -go -setboard 5rk1/r2qnppp/p1nN4/2Q5/3PB3/4P3/6PP/R4RK1 w - - -solution Nxf7 -id "ECM.768" -go -setboard 1bq2k2/1b1n1pp1/pp5p/2pBpN2/P3P2B/3Q1P2/1PP3PP/4K3 w - - -solution Bxf7 -id "ECM.769" -go -setboard rnb2rk1/pp3pbp/2p3pB/2q5/4P3/1BN5/PPP3PP/R2Q1R1K w - - -solution Rxf7 -id "ECM.770" -go -setboard 3qr1k1/1br1bp1p/p3p1pB/1p1nN3/3PB3/7Q/PP3PPP/3RR1K1 w - - -solution Nxf7 -id "ECM.771" -go -setboard 1qr1b1k1/4bpp1/pn2p2p/1p1nN3/3P4/P2BBN1Q/1P3PPP/4R1K1 w - - -solution Bxh6 -id "ECM.772" -go -setboard rr1q2k1/1p2bpp1/2p1p2p/P1Pn4/2NP4/3Q1RP1/5PKP/2B1R3 w - - -solution Bxh6 -id "ECM.773" -go -setboard r2r2k1/pb1nbpp1/1qp1pn1p/1p2N3/3P1B2/P1N1P3/BPQ2PPP/2R2RK1 w - - -solution Bxh6 -id "ECM.774" -go -setboard 2r5/1p4bk/3p2rp/4pN2/1P2P1pR/2P2q2/QP6/1K5R w - - -solution Rxh6+ -id "ECM.775" -go -setboard r1b1r3/pp2Npbk/3pp2p/q5p1/2QNPP2/6P1/PPP3P1/2KR3R w - - -solution Ndf5 -id "ECM.776" -go -setboard r1q3r1/1ppQ2pk/3bNp1p/p3pP2/P3P3/7R/1PP3PP/3R2K1 w - - -solution Rxh6+ -id "ECM.777" -go -setboard 4r1k1/p1pq1pp1/2p5/3p1b2/Q7/2P1B2P/P1P1rPP1/2R2RK1 b - - -solution Bxh3 -id "ECM.778" -go -setboard 1rbqnr2/5p1k/p1np3p/1p1Np3/4P2P/1Q2B3/PPP1BP2/2KR3R w - - -solution Bxh6 -id "ECM.779" -go -setboard r1br2k1/p1q2pp1/4p1np/2ppP2Q/2n5/2PB1N2/2P2PPP/R1B1R1K1 w - - -solution Bxh6 -id "ECM.780" -go -setboard 3r1r1k/p5p1/n1p2p1p/1qp2b2/N1R4R/1PB1P1P1/P2P3P/3Q2K1 w - - -solution Rxh6+ -id "ECM.781" -go -setboard r4rk1/2q1b1p1/p4p1p/n3pP2/1p2Q1N1/4B3/PPP3PP/4RR1K w - - -solution Nxh6+ -id "ECM.782" -go -setboard 6rk/3b1n1p/1p1q3b/1PpNp3/2P1Pp2/2Q2NrP/5RP1/2R2B1K b - - -solution Bxh3 -id "ECM.783" -go -setboard r2q1rk1/ppp2pp1/1b2b2p/3n3Q/2Bp4/3P1N2/PPP2PPP/R1B1R1K1 w - - -solution Bxh6 -id "ECM.784" -go -setboard r3rbk1/1bp1qpp1/p6p/np2p2Q/4P2N/1BP4P/PP3PP1/R1B1R1K1 w - - -solution Bg5 -id "ECM.785" -go -setboard 4q3/p2r1ppk/R6p/3n4/3B1Q2/4P2P/5PP1/6K1 w - - -solution Rxh6+ -id "ECM.786" -go -setboard 7r/1p2qk2/pQ1p2p1/3Ppp2/P4b2/2P2PrP/1P2R1B1/4R2K b - - -solution Rhxh3+ -id "ECM.787" -go -setboard 5rk1/Qp5p/3pp1p1/4n3/2P1P2q/5r1P/P3NPP1/3R1RK1 b - - -solution Rxh3 -id "ECM.788" -go -setboard 2r1r1k1/pb1n1pp1/1p1qpn1p/4N1B1/2PP4/3B4/P2Q1PPP/3RR1K1 w - - -solution Bxh6 -id "ECM.789" -go -setboard 3q1rk1/p2bbpp1/1rn4p/1pp2P2/3pBBP1/3P3P/PPPQ3N/R4RK1 w - - -solution Bxh6 -id "ECM.790" -go -setboard 2br2k1/4qppp/p5r1/2pBp3/2P1Pn2/4Q2P/RP3PPK/1N4R1 b - - -solution Bxh3 -id "ECM.791" -go -setboard r2qr3/2p1b1pk/p5pp/1p2p3/nP2P1P1/1BP2RP1/P3QPK1/R1B5 w - - -solution Bxh6 -id "ECM.792" -go -setboard r5r1/pbn4k/1p1p1Ppp/2pPn3/4BQq1/1PN3P1/PB3PK1/3R1R2 w - - -solution Qxh6+ -id "ECM.793" -go -setboard r1b2rk1/pp2bpp1/4p2p/2q4Q/5nNB/2PB4/PP3PPP/2KR3R w - - -solution Nxh6+ -id "ECM.794" -go -setboard 6k1/p4ppp/1pn1b1rr/8/PBpPp2q/2P1P1NP/5PP1/RR3QK1 b - - -solution Bxh3 -id "ECM.795" -go -setboard r1b2rk1/1p3pp1/p5Pp/2bpPp1Q/3N4/1Pq1B1R1/2P4P/3R2K1 w - - -solution Bxh6 -id "ECM.796" -go -setboard r2r2k1/pp1n1bp1/2p2p1p/b4N2/q2BR3/2QB2PP/1PP5/2KR4 w - - -solution Nxh6+ -id "ECM.797" -go -setboard 3r2bk/1q4p1/p2P1N1p/2p1rP2/pb5R/7P/1P4P1/2Q2RK1 w - - -solution Rxh6+ -id "ECM.798" -go -setboard 2b1rk2/r6p/1pP1p1p1/p2pNnR1/5Q2/P1B4q/1PP2P1P/1K4R1 w - - -solution Nxg6+ -id "ECM.799" -go -setboard 6R1/6Q1/3q2p1/5p1p/P3p1k1/1P1r2P1/5PK1/8 b - - -solution Rxg3+ -id "ECM.800" -go -setboard r6r/ppnqpk2/3pbpp1/5N2/1PP1P3/5RR1/P2QB1PP/6K1 w - - -solution Nh6+ -id "ECM.801" -go -setboard 3r3r/p4pk1/5Rp1/3q4/1p1P2RQ/5N2/P1P4P/2b4K w - - -solution Rfxg6+ -id "ECM.802" -go -setboard 2b2r1k/2p3pp/2Nb4/pP5q/2PP4/P4pP1/5P1P/R1BQ2K1 b - - -solution Bxg3 -id "ECM.803" -go -setboard r1bq2k1/pp3r1p/2n1p1p1/3pP3/6Q1/2PB2P1/P4PK1/R1B4R w - - -solution Bxg6 -id "ECM.804" -go -setboard 3q1rk1/2r4p/n1p1n1pQ/p2pP3/Np1P2R1/5PN1/PP3KP1/2R5 w - - -solution Rxg6+ -id "ECM.805" -go -setboard 2Nq1rk1/6pp/b1p2b2/p6Q/np1p4/6P1/PB3PBP/R3R1K1 w - - -solution Be4 -id "ECM.806" -go -setboard r2r4/p4pk1/2p3p1/1p1nPR2/5p1Q/2N5/PPPq4/1K4R1 w - - -solution e6 -id "ECM.807" -go -setboard 2kr3r/pbq3p1/1p2pp2/2b4n/2P1B3/1P1N2Pp/PB2QP1P/R4RK1 b - - -solution Nxg3 -id "ECM.808" -go -setboard r5k1/1p2Rpb1/3p1np1/2nP2B1/1qP5/1pN1Q2P/P5P1/1B4K1 w - - -solution Bxg6 -id "ECM.809" -go -setboard 2rqr1k1/pb2bp1p/1pn1pnpB/4N3/3P4/P1N3R1/1P3PPP/RB1Q2K1 w - - -solution Rxg6+ -id "ECM.810" -go -setboard 2r5/pp2qr1k/1nb3pp/2ppBp2/2P4P/3B2R1/P3QPP1/1R4K1 w - - -solution Rxg6 -id "ECM.811" -go -setboard 3rr3/3q1p1k/p2P2pp/1bp1b3/5N2/6QP/P1B3P1/3RR1K1 w - - -solution Bxg6+ -id "ECM.812" -go -setboard r2q1rk1/pp2ppb1/3pn1pQ/3R4/2P3B1/4BR2/PP4PP/6K1 w - - -solution Qxg6 -id "ECM.813" -go -setboard 1n1b1rk1/r4p1p/p1p2qpQ/P3Np2/2BP4/2P1R3/5PPP/R5K1 w - - -solution Rb1 -id "ECM.814" -go -setboard 6rk/pb3p1p/3bpPq1/3P4/1p1B3r/1B4P1/PPRQ1R1P/6K1 b - - -solution Bxg3 -id "ECM.815" -go -setboard 3rr1k1/pb2bp1p/3qp1pB/1p2N3/3P2Q1/2P1R3/P4PPP/4R1K1 w - - -solution Nxg6 -id "ECM.816" -go -setboard r2q1rk1/pb1nbp1p/1p2p1p1/4N1BQ/2PP4/P7/5PPP/RB2R1K1 w - - -solution Nxg6 -id "ECM.817" -go -setboard 1Q4R1/r2qbp2/3p1kp1/3Bp2p/8/3PP1P1/5PKP/8 w - - -solution Rxg6+ -id "ECM.818" -go -setboard r3r1k1/b1p2pn1/p2q1Bp1/1p1bN1Qp/3P4/2P4P/PPB2PP1/R3R1K1 w - - -solution Nxg6 -id "ECM.819" -go -setboard 3r2k1/p1rn1p1p/1p2pp2/6q1/3PQNP1/5P2/P1P4R/R5K1 w - - -solution Nxe6 -id "ECM.820" -go -setboard k2r3r/ppq2p1p/n1pb1Pp1/4p3/2Q5/1R2B1P1/PPP2PBP/R5K1 w - - -solution Rxb7 -id "ECM.821" -go -setboard 1rb2rk1/p1p1qppp/2p2n2/3P4/1b3P2/2NB4/PPPBQ1PP/2KR3R b - - -solution Ba3 -id "ECM.822" -go -setboard k7/pp1N4/4P3/5P2/8/5p2/1R6/B4Knq w - - -solution Nb6+ -id "ECM.823" -go -setboard 4rrk1/p5pp/1p4q1/3p2n1/2pP2P1/P1P1PP2/3B2K1/1R2QR2 b - - -solution Nxf3 -id "ECM.824" -go -setboard 1q1r3k/3P1pp1/ppBR1n1p/4Q2P/P4P2/8/5PK1/8 w - - -solution Rxf6 -id "ECM.825" -go -setboard 1rbq2k1/b1p3pp/p1Pp4/3Ppr2/8/5N2/PPQ2PPP/R1B2RK1 b - - -solution Rxf3 -id "ECM.826" -go -setboard 1r1q1r1k/2p3pp/1p1bQ3/p7/1PP5/Pn3B2/1B3PPP/R4RK1 b - - -solution Rxf3 -id "ECM.827" -go -setboard r4rk1/2p1qpp1/p1p5/2Ppn2b/2PQ1N1p/4P3/PB3PPP/R4RK1 b - - -solution Nf3+ -id "ECM.828" -go -setboard r1bq1rk1/1p2bppp/p3p3/n3P3/4N3/1P1P1N2/PB4PP/R3QR1K w - - -solution Nf6+ -id "ECM.829" -go -setboard 1n2r3/p1pq1kp1/1b1pNpp1/3P4/5RP1/3Q3P/1B3P2/6K1 w - - -solution Bxf6 -id "ECM.830" -go -setboard r1bq1rk1/ppp3p1/7p/3P2n1/2PQ1p2/1N5P/PPP2PPK/R1B2R2 b - - -solution Nf3+ -id "ECM.831" -go -setboard 3r1b2/p4bkp/1p1P1p2/r3p1p1/2q1N3/2N2P2/1P1R2PP/2QR3K w - - -solution Nxf6 -id "ECM.832" -go -setboard rqbn1rk1/1p3ppp/p3p3/8/4NP2/8/PPP1BQPP/1K1R3R w - - -solution Nf6+ -id "ECM.833" -go -setboard r1b1kb1r/1p4pp/p2ppn2/8/2qNP3/2N1B3/PPP3PP/R2Q1RK1 w kq - -solution Rxf6 -id "ECM.834" -go -setboard 1rn5/p3Bk1p/1pq1bpp1/P3p3/1Q2P2P/2P3P1/5PB1/1R4K1 w - - -solution Bxf6 -id "ECM.835" -go -setboard 2nr2k1/1pq1bppp/p1p5/2p1P1PQ/2P1NP2/1PNR4/P6P/6K1 w - - -solution Nf6+ -id "ECM.836" -go -setboard 6nk/pn2qr1r/1pbp1p1p/2p1pPpN/P1P1P1PP/2PP3R/7Q/2BBK2R w K - -solution Nxf6 -id "ECM.837" -go -setboard 7r/4p1k1/p3Pppp/1p6/3N1R2/3PQ2P/qr4b1/5RK1 w - - -solution Rxf6 -id "ECM.838" -go -setboard r3k2r/1bq1bp2/p3p1pp/1p2P3/3NpP2/4B1Q1/PPP3PP/1K1R3R w kq - -solution Nxe6 -id "ECM.839" -go -setboard rnbqkb1r/pp1n1p2/4p1p1/1N1pP2p/1P4Q1/3B1N2/P1PB1PPP/R3K2R w KQkq - -solution Qxe6+ -id "ECM.840" -go -setboard r1b1kb1r/1p1n1ppp/p2ppn2/6BB/2qNP3/2N5/PPP2PPP/R2Q1RK1 w kq - -solution Nxe6 -id "ECM.841" -go -setboard r1b1k1nr/3n1p1p/1qpBp1p1/pp2b3/4PN2/PBN2Q2/1PP2PPP/2KR3R w kq - -solution Nxe6 -id "ECM.842" -go -setboard 2r2rk1/1b3ppp/pq2p3/1pbn2N1/3B3P/1B4P1/P1P1QP2/2KRR3 w - - -solution Qh5 -id "ECM.843" -go -setboard r3k2r/pp1n1pb1/1qn1p1p1/2p3Pp/4R2P/2NP2QB/PPPB1P2/2K1R3 w kq - -solution Rxe6+ -id "ECM.844" -go -setboard r3k2r/1q3ppp/p3p3/2b1P3/p2N1Q2/P7/1PP3PP/3R1R1K w kq - -solution Nxe6 -id "ECM.845" -go -setboard 3qr1k1/rp3pbp/2p3p1/3p1b2/N1nP4/P2NP3/1PQ2PPP/1BRR2K1 b - - -solution Rxe3 -id "ECM.846" -go -setboard rnbqkb1r/1p1n1pp1/p3p2p/3pP3/3p1NQP/2N5/P1PB1PP1/R3KB1R w KQkq - -solution Nxe6 -id "ECM.847" -go -setboard 3r1b1R/3bkpp1/1p1np3/1BqpPPP1/PQ1N4/8/1PP5/1K6 w - - -solution f6+ -id "ECM.848" -go -setboard 4r1k1/pp2n2p/1r1n1bp1/q2p1pB1/3P3N/P1NQ1P1P/1PP3R1/1K5R b - - -solution Rxb2+ -id "ECM.849" -go -setboard 3r2k1/2p1bppp/4b3/p2pP3/qr1N1B1P/8/1PPQ2P1/1K1RR3 b - - -solution Rxb2+ -id "ECM.850" -go -setboard 1k1r3r/ppR2ppp/1q2b3/1N6/4Q3/8/PP3PPP/4R1K1 w - - -solution Rxb7+ -id "ECM.851" -go -setboard 1r3rk1/5pbp/p2Pp1p1/q1n3P1/2p2P1P/2N1Q2R/PP6/1BKR4 b - - -solution Rxb2 -id "ECM.852" -go -setboard rr4k1/5p1p/n2p1npQ/q1pP4/6P1/2N2P1N/PP1R3P/2K4R b - - -solution Rxb2 -id "ECM.853" -go -setboard 1k2bb1r/1p3p1p/p2qpP1p/3pN3/3P1Q2/2RB2P1/PP3P1P/6K1 w - - -solution Qc1 -id "ECM.854" -go -setboard 1r4k1/1r3p2/p1p1b1pQ/q3P3/P7/bPN1R1P1/2P4P/1K1R1B2 b - - -solution Bxb3 -id "ECM.855" -go -setboard 5rk1/5rp1/p2qb3/R2Np2Q/3bP2B/7P/P5PK/1R6 b - - -solution Rf1 -id "ECM.856" -go -setboard 6k1/5p2/R3p1p1/1pq1PpB1/4nP2/5Q1P/P2r2PK/8 w - - -solution Ra8+ -id "ECM.857" -go -setboard 3r1rk1/p3q1pp/2Rb4/3P4/8/1P2Qp2/PB3PPP/3R2K1 b - - -solution Bxh2+ -id "ECM.858" -go -setboard 1r6/5pk1/p1b2qpp/P1P1n3/1N6/Q5NP/5PP1/3R2K1 b - - -solution Bxg2 -id "ECM.859" -go -setboard 1r4k1/1q3pb1/r1b3p1/pp1Qp3/P3P3/1B2BP2/1PP3P1/1K1R3R w - - -solution Qd8+ -id "ECM.860" -go -setboard 2R5/p3q1kp/6p1/3p4/b2P2p1/Pr4N1/5Q2/3K3R w - - -solution Nf5+ -id "ECM.861" -go -setboard 3r4/kb6/pp6/4R1pr/PPpq4/2N3Q1/2P3PP/3R3K b - - -solution Rf8 -id "ECM.862" -go -setboard 4r1k1/5p1p/4q1p1/8/Q5n1/3B2P1/P4P1P/2R3K1 b - - -solution Nxf2 -id "ECM.863" -go -setboard 8/2q1np1k/6p1/1PPn3p/8/P2Q3P/6P1/2R4K b - - -solution Nf5 -id "ECM.864" -go -setboard r1bq1rk1/ppp1n1bn/3p2p1/3Pp1Pp/1PP1Pp1P/2NN1P2/P3B3/R1BQ1RK1 b - - -solution Nxg5 -id "ECM.865" -go -setboard 7R/1p1r4/2b2p2/2Pp1qk1/3P1bp1/3N4/2Q2PB1/6K1 w - - -solution Rg8+ -id "ECM.866" -go -setboard r6r/1pn1b1k1/2p1pq2/2Pp3p/p2P1Pp1/P2BP1P1/1P2QB2/3R2KR w - - -solution Rxh5 -id "ECM.867" -go -setboard 1r4k1/pp4pp/2n1b3/1Rq1p3/2P2r2/P1B2P2/2Q1B1PP/3R1K2 b - - -solution Rxf3+ -id "ECM.868" -go -setboard 7k/1p4p1/2p4r/5p2/p1QP1q2/2P2PR1/r4P1P/4R2K b - - -solution Rxh2+ -id "ECM.869" -go -setboard 1r3r2/6kp/3p1pp1/qnp1pP2/1p2P2N/3P4/PPPQ1P1P/1K1R2R1 w - - -solution fxg6 -id "ECM.870" -go -setboard 5rk1/1rP3pp/p4n2/3Pp3/1P2Pq2/2Q4P/P5P1/R3R1K1 b - - -solution Rxc7 -id "ECM.871" -go -setboard 2rq1rk1/5ppp/2b1pb2/np6/6N1/2NBB3/PP3PPP/R2Q2K1 w - - -solution Bb6 -id "ECM.872" -go -setboard 5r2/p2p2kp/3PnNp1/1qr1Pp2/2p5/P1R5/6PP/2Q1R1K1 w - - -solution Qh6+ -id "ECM.873" -go -setboard 2Qbq1k1/6p1/1p4Np/4p2P/3rP3/8/3p2PK/3R4 w - - -solution Rf1 -id "ECM.874" -go -setboard 2kr1br1/ppp2p1p/2n1p1q1/2Nn4/3P4/1Q2PB2/PP1B1P1P/2R1K2R b K - -solution Qg1+ -id "ECM.875" -go -setboard 2K5/6p1/kp2P3/1p6/1P6/2P1P2p/8/1r6 w - - -solution e7 -id "ECM.876" -go -setboard 2kr3r/1Rp1bpp1/p6q/Q3P3/2P5/3p1NP1/P3P2P/1R4K1 b - - -solution Qe3+ -id "ECM.877" -go -setboard 6k1/1R4pp/2p5/8/P1rNp3/6Pb/4PK2/8 w - - -solution a5 -id "ECM.878" -go -setboard r2r2k1/pp3ppp/2q5/2p5/2P1P1n1/P4P1b/1BQ1BP1P/R3R2K b - - -solution Rd2 -id "ECM.879" -go diff --git a/src/unix.c b/src/unix.c index 99216d0..68d2319 100644 --- a/src/unix.c +++ b/src/unix.c @@ -27,8 +27,6 @@ Revision History: #include #include #include -#include -#include #include #include #include @@ -1020,44 +1018,54 @@ SystemReleaseLock(ULONG u) } +/* + * These "semaphores" only ever signal between threads of this same + * process (see input.c's producer/consumer wait between the stdin + * reader thread and the command-dispatch thread) -- there is no + * cross-process handoff here, despite the SysV-semaphore-shaped API. + * A SysV semget(IPC_PRIVATE, ...) set is kernel-global, outlives the + * process, and only gets torn down by an explicit IPC_RMID -- so a + * killed (rather than gracefully quit()'d) process leaks it, and on a + * shared box with a low kern.ipc.semmni it doesn't take many leaked + * engines to exhaust the system-wide semaphore-set table. Every + * subsequent semget() then fails, and _WaitUntilTheresInputToRead + * (input.c) silently falls back to 100ms-polling instead of blocking + * on a real wait -- a ~1000x slowdown that looks exactly like machine + * contention. A pthread_mutex_t/pthread_cond_t pair does the same + * counting-semaphore job entirely in-process, with no kernel-persistent + * object to leak: the OS reclaims it unconditionally the instant the + * process dies, SIGKILL or not. + */ #define MAX_SEM (8) -int g_rgSemaphores[MAX_SEM]; -#ifdef DEBUG -ULONG g_rgSemValues[MAX_SEM]; -#endif + +typedef struct _SEM_ENTRY +{ + FLAG fInUse; + ULONG uCount; + pthread_mutex_t lock; + pthread_cond_t cond; +} +SEM_ENTRY; + +SEM_ENTRY g_rgSemaphores[MAX_SEM]; ULONG -SystemCreateSemaphore(ULONG uValue) +SystemCreateSemaphore(ULONG uValue) { ULONG u; - union semun { - int val; - struct semid_ds *buf; - unsigned short *array; - } value; if (uValue > MAX_USHORT) return((ULONG)-1); LOCK_SYSTEM; - for (u = 0; u < MAX_SEM; u++) + for (u = 0; u < MAX_SEM; u++) { - if (g_rgSemaphores[u] < 0) + if (g_rgSemaphores[u].fInUse == FALSE) { - g_rgSemaphores[u] = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600); - if (g_rgSemaphores[u] < 0) - { - u = (ULONG)-1; - goto end; - } - value.val = uValue; - if (semctl(g_rgSemaphores[u], 0, SETVAL, value) < 0) - { - (void)SystemDeleteSemaphore(u); - u = (ULONG)-1; - goto end; - } + pthread_mutex_init(&(g_rgSemaphores[u].lock), NULL); + pthread_cond_init(&(g_rgSemaphores[u].cond), NULL); + g_rgSemaphores[u].uCount = uValue; + g_rgSemaphores[u].fInUse = TRUE; #ifdef DEBUG - g_rgSemValues[u] = uValue; - Trace("Semaphore #%u created, initial value is %u\n", + Trace("Semaphore #%u created, initial value is %u\n", u, uValue); #endif goto end; @@ -1071,82 +1079,60 @@ SystemCreateSemaphore(ULONG uValue) FLAG SystemDeleteSemaphore(ULONG u) { - union semun { - int val; - struct semid_ds *buf; - unsigned short *array; - } value; FLAG fRet = TRUE; - memset(&value, 0, sizeof(value)); LOCK_SYSTEM; - if ((u < MAX_SEM) && (g_rgSemaphores[u] >= 0)) + if ((u < MAX_SEM) && (g_rgSemaphores[u].fInUse == TRUE)) { - if (semctl(g_rgSemaphores[u], 0, IPC_RMID, value) < 0) - { - fRet = FALSE; - } - else - { - g_rgSemaphores[u] = -1; - } + pthread_mutex_destroy(&(g_rgSemaphores[u].lock)); + pthread_cond_destroy(&(g_rgSemaphores[u].cond)); + g_rgSemaphores[u].fInUse = FALSE; + } + else + { + fRet = FALSE; } UNLOCK_SYSTEM; return(fRet); } void -SystemReleaseSemaphoreResource(ULONG u) +SystemReleaseSemaphoreResource(ULONG u) { - struct sembuf operation; - LOCK_SYSTEM; - if ((u < MAX_SEM) && (g_rgSemaphores[u] >= 0)) + if ((u < MAX_SEM) && (g_rgSemaphores[u].fInUse == TRUE)) { - operation.sem_num = u; - operation.sem_op = +1; - operation.sem_flg = 0; - if (semop(g_rgSemaphores[u], &operation, 1) < 0) - { - UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE, - NULL, "semop", (void *)errno, (void *)0, - __FILE__, __LINE__); - } + pthread_mutex_lock(&(g_rgSemaphores[u].lock)); + g_rgSemaphores[u].uCount += 1; #ifdef DEBUG - g_rgSemValues[u] += 1; - Trace("Semaphore #%u value incremented to %u\n", u, - g_rgSemValues[u]); + Trace("Semaphore #%u value incremented to %u\n", u, + g_rgSemaphores[u].uCount); #endif + pthread_cond_signal(&(g_rgSemaphores[u].cond)); + pthread_mutex_unlock(&(g_rgSemaphores[u].lock)); } UNLOCK_SYSTEM; } void -SystemObtainSemaphoreResource(ULONG u) +SystemObtainSemaphoreResource(ULONG u) { - struct sembuf operation; - LOCK_SYSTEM; - if ((u < MAX_SEM) && (g_rgSemaphores[u] >= 0)) + if ((u < MAX_SEM) && (g_rgSemaphores[u].fInUse == TRUE)) { - operation.sem_num = u; - operation.sem_op = -1; - operation.sem_flg = 0; + pthread_mutex_lock(&(g_rgSemaphores[u].lock)); UNLOCK_SYSTEM; - if (semop(g_rgSemaphores[u], &operation, 1) < 0) + while (g_rgSemaphores[u].uCount == 0) { - UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE, - NULL, "semop", (void *)errno, (void *)1, - __FILE__, __LINE__); + pthread_cond_wait(&(g_rgSemaphores[u].cond), + &(g_rgSemaphores[u].lock)); } + g_rgSemaphores[u].uCount -= 1; #ifdef DEBUG - if (g_rgSemValues[u] > 0) - { - g_rgSemValues[u] -= 1; - Trace("Semaphore #%u value decremented to %u\n", u, - g_rgSemValues[u]); - } + Trace("Semaphore #%u value decremented to %u\n", u, + g_rgSemaphores[u].uCount); #endif + pthread_mutex_unlock(&(g_rgSemaphores[u].lock)); return; } UNLOCK_SYSTEM; @@ -1187,9 +1173,9 @@ Return value: memset(g_AllocHash, 0, sizeof(g_AllocHash)); #endif - for (u = 0; u < MAX_SEM; u++) + for (u = 0; u < MAX_SEM; u++) { - g_rgSemaphores[u] = -1; + g_rgSemaphores[u].fInUse = FALSE; } InitializeListHead(&g_SystemThreadList); pthread_mutex_init(&g_SystemLock, NULL); -- cgit v1.3